您的位置:华清远见教育科技集团 >> Android资料 >> Java Synchronized同步关键字的用法  
 
Java Synchronized同步关键字的用法
分享到:

synchronized关键字属于操作系统的范畴,与同步对应的是异步。在程序设计的概念中,有同步调用或者异步调用,同步是指该段代码(方法)从调用开始,直到内部执行完毕后才能返回;异步调用是指调用该段代码(方法)后立即返回,无论该段代码内部所执行的物理操作是否执行完毕,异步代码一般存在于多线程程序设计中,单线程程序内部不存在异步调用。

synchronized关键字的作用就是告诉操作系统,在执行该关键字所限定的代码片段内,不允许被其他线程打断。在一般的操作系统设计中,会提供一个类似于synchronized的API方法,而Java则是给了这样一个关键字,相当于说,Java编译器为操作系统分担了一部分工作。

那么,什么情况下需要使用synchronized关键字呢?凡是需要某段代码在执行时不被其他线程打断时,都可以加上synchronized关键字,举例如代码清单1-1所示。

代码清单1-1 synchronized关键字

public class MyMusicWidgetProvider{
        private static MyMusicWidgetProvider sInstance;
        static synchronized MyMusicWidgetProvider getInstance(){
            if(sInstance==null)
                sInstance=new MyMusicWidgetProvider();
            returnsInstance;
        }
    }

以上代码定义了一个类,并希望该类在运行时仅有一个实例,每次调用该类所包含的方法时,先得到该类的实例,然后再通过实例调用其他方法。该类有一个static的getInstance()方法,该方法的作用就是检查是否存在一个实例,如果没有就创建一个,否则返回存在的实例。

getInstance()方法前面加了synchronized关键字,为的是该方法在执行时不能被其他线程打断,那么,为什么有这样的要求呢?试想一下,假设没有使用该关键字,第1个线程A 在执行getInstance()方法时,方法体中的new 运算符刚刚创建了一个MyMusicWidgetProvider实例,但还没来得及把实例赋值给sInstance,而此时B线程又来调用getInstance()方法,出现这种情况的原因是:sInstance= new MyMusicWidgetProvider()这句代码会被Java编译器编译成多条机器指令,其中给sInstance赋值的机器指令和创建一个MyMusicWidgetProvider对象的机器指令是分开的。此时对于B线程来讲,它会重新检查sInstance是否为空,由于A线程还没有来得及给sIntance赋值,因此,B线程就会再次创建一个新的MyMusicWidgetProvider实例,当B线程暂停并返回A 线程时,A 线程会把第1次创建的实例重新赋值给sInstance,这就会导致B线程将来对MyMusicWidgetProvider实例的错误引用,因为B线程所创建实例的地址被A线程修改了。

因此,这就要求getInstance()一旦开始执行,必须执行完毕后才能返回,中间不能被其他线程打断。

sychronized除了约束整个方法外,也可以约束一小段代码,这样做的好处有两个:一个是可以避免定义新方法;另一个是能够避免一些线程同步问题,如代码清单1-2所示。

代码清单1-2 synchronized约束小段代码

Object obj;
    int c;
    void addMethod(){
        synchronized(obj){
            c++;
            //其他代码
        }
    }

obj可以是任意类型的一个对象,该对象的唯一作用就是给synchronized提供一个“锁”。同步设计的原则之一是尽量减少同步中包含的代码大小,从而在线程间能够更平衡地运行。因此,同步一小段代码是一个不错的做法。同时,假设addMethod()内部还调用了其他同步方法,那么,从这个同步跳到另外的同步会增加线程间死锁的风险,因此不同步整个addMethod()而仅同步局部代码,这就是第2个好处。

扩展阅读:关于共享资源的同步访问安全问题

1. 定义private 的instance变量+它的 get方法,而不要定义public/protected的instance变量。如果将变量定义为public,对象在外界可以绕过同步方法的控制而直接取得它,并改动它。这也是JavaBean的标准实现方式之一。

2. 如果instance变量是一个对象,如数组或ArrayList什么的,那上述方法仍然不安全,因为当外界对象通过get方法拿到这个instance对象的引用后,又将其指向另一个对象,那么这个private变量也就变了,岂不是很危险。 这个时候就需要将get方法也加上synchronized同步,并且,只返回这个private对象的clone()――这样,调用端得到的就是对象副本的引用了。

 更多相关文章

·Android Map类的使用方法
·Abstract class和Interface的使用
·Android Interface的使用
·Android程序UI设计
·AndroidManifest.xml文件详解