java的线程同步机制synchronized关键字的理解

线程同步:
       
        因为同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题。Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问。

须要明确的几个问题:

       1)synchronized关键字能够做为函数的修饰符,也可做为函数内的语句,也就是平时说的同步方法和同步语句块。若是 再细的分类,synchronized可做用于instance变量、object reference(对象引用)、static函数和class literals(类名称字面常量)身上。
        2)不管synchronized关键字加在方法上仍是对象上,它取得的锁都是对象,而不是把一段代码或函数看成锁――并且同步方法极可能还会被其余线程的对象访问。
        3)每一个对象只有一个锁(lock)与之相关联。
        4)实现同步是要很大的系统开销做为代价的,甚至可能形成死锁,因此尽可能避免无谓的同步控制。

数组

一、synchronized关键字的做用域有二种:

        1)是某个对象实例内,synchronized aMethod(){}能够防止多个线程同时访问这个对象的synchronized方法(若是一个对象有多个synchronized方法,只要一个线 程访问了其中的一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法)。这时,不一样的对象实例的 synchronized方法是不相干扰的。也就是说,其它线程照样能够同时访问相同类的另外一个对象实例中的synchronized方法;

        2)是某个类的范围,synchronized static aStaticMethod{}防止多个线程同时访问这个类中的synchronized static 方法。它能够对类的全部对象实例起做用。
安全


        synchronized 方法控制对类成员变量的访问:每一个类实例对应一把锁,每一个 synchronized 方法都必须得到调用该方法的类实例的锁方能执行,不然所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能得到 该锁,从新进入可执行状态。这种机制确保了同一时刻对于每个类实例,其全部声明为 synchronized 的成员函数中至多只有一个处于可执行状态(由于至多只有一个可以得到该类实例对应的锁),从而有效避免了类成员变量的访问冲突(只要全部可能访问类成员变 量的方法均被声明为 synchronized)。
  
  在 Java 中,不光是类实例,每个类也对应一把锁,这样咱们也可将类的静态成员函数声明为 synchronized ,以控制其对类的静态成员变量的访问。
  
   synchronized 方法的缺陷:同步方法,这时synchronized锁定的是哪一个对象呢?它锁定的是调用这个同步方法对象。也就是说,当一个对象 P1在不一样的线程中执行这个同步方法时,它们之间会造成互斥,达到同步的效果。可是这个对象所属的Class所产生的另外一对象P2却能够任意调用这个被加 了synchronized关键字的方法.同步方法实质是将synchronized做用于object reference。――那个拿到了P1对象锁的线程,才能够调用P1的同步方法,而对P2而言,P1这个锁与它绝不相干,程序也可能在这种情形下摆脱同步机制的控制,形成数据混乱:(;若将一个大的方法声明为synchronized 将会大大影响效率,典型地,若将线程类的方法 run() 声明为 synchronized ,因为在线程的整个生命期内它一直在运行,所以将致使它对本类任何 synchronized 方法的调用都永远不会成功。固然咱们能够经过将访问类成员变量的代码放到专门的方法中,将其声明为 synchronized ,并在主方法中调用来解决这一问题,可是 Java 为咱们提供了更好的解决办法,那就是 synchronized 块。
多线程


 二、除了方法前用synchronized关键字,synchronized关键字还能够用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。用法是: synchronized(this){/*区块*/},它的做用域是当前对象。函数


        这时锁就是对象,谁拿到这个锁谁就能够运行它所控制的那段代码。当有一个明确的对象做为锁时,就能够这样写程序,但当没有明确的对象做为锁,只是想让一段代码同步时,能够建立一个特殊的instance变量(它得是一个对象)来充当锁:
class Foo implements Runnable
{
       private byte[] lock = new byte[0]; // 特殊的instance变量
    Public void methodA()
{
       synchronized(lock) { //… }
}
//…..
}
注:零长度的byte数组对象建立起来将比任何对象都经济――查看编译后的字节码:生成零长度的byte[]对象只需3条操做码,而Object lock = new Object()则须要7行操做码。
this


3.将synchronized做用于static 函数,示例代码以下:
      Class Foo 
{
public synchronized static void methodAAA()   // 同步的static 函数
{
//….
}
public void methodBBB()
{
       synchronized(Foo.class)   // class literal(类名称字面常量)
}    }
       代码中的methodBBB()方法是把class literal做为锁的状况,它和同步的static函数产生的效果是同样的,取得的锁很特别,是当前调用这个方法的对象所属的类(Class,而再也不是由这个Class产生的某个具体对象了)。
spa


可 以推断:若是一个类中定义了一个synchronized的static函数A,也定义了一个synchronized 的instance函数B,那么这个类的同一对象Obj在多线程中分别访问A和B两个方法时,不会构成同步,由于它们的锁都不同。B方法的锁是Obj这 个对象,而B的锁是Obj所属的那个Class。线程


对共享资源的同步访问更加安全的技巧:
对象



        1) 定义private 的instance变量+它的 get方法,而不要定义public/protected的instance变量。若是将变量定义为public,对象在外界能够绕过同步方法的控制而直接取得它,并改动它。这也是JavaBean的标准实现方式之一。
        2)如 果instance变量是一个对象,如数组或ArrayList什么的,那上述方法仍然不安全,由于当外界对象经过get方法拿到这个instance对 象的引用后,又将其指向另外一个对象,那么这个private变量也就变了,岂不是很危险。 这个时候就须要将get方法也加上synchronized同步,而且,只返回这个private对象的clone()――这样,调用端获得的就是对象副 本的引用了。
继承


补充
进程


synchronized关键字是不能继承的,也就是说,基类的方法synchronized f(){} 在继承类中并不自动是synchronized f(){},而是变成了f(){}。继承类须要你显式的指定它的某个方法为synchronized方法。 

相关文章
相关标签/搜索