JAVA中synchronized关键字能够作为函数的修饰符,也可作为函数内的语句,也就是平时说的同步方法和同步语句块。假如再细的分类,synchronized可作用于instance变量、object reference(对象引用,例如this)、static函数和class literals(类名称字面常量)身上。下面讨论synchronized用到不同地方对代码产生的影响:
假设P1、P2是同一个类的不同对象,这个类中定义了以下几种情况的同步块或同步方法,P1、P2就都能够调用他们。
1.把synchronized当作函数修饰符时,示例代码如下:
public synchronized void method(){ //…. }
这也就是同步方法,那这时synchronized锁定的是哪个对象呢?他锁定的是调用这个同步方法对象。也就是说,当一个对象P1在不同的线程中执行这个同步方法时,他们之间会形成互斥,达到同步的效果。但是这个对象所属的Class所产生的另一对象P2却能够任意调用这个被加了synchronized关键字的方法。上边的示例代码等同于如下代码:
public void method(){ synchronized (this) // (1){ //….. } }
(1)处的this指的是什么呢?他指的就是调用这个方法的对象,如P1。可见,同步方法实质是将synchronized作用于Object Reference。那个拿到了P1对象锁的线程,才能够调用P1的同步方法,而对P2而言,P1这个锁和他毫不相干,程式也可能在这种情形下摆脱同步机制的控制,造成数据混乱。
2.同步块,示例代码如下:
public void method(SomeObject so) { synchronized(so){ //….. } }
这时,锁就是so这个对象,谁拿到这个锁谁就能够运行他所控制的那段代码。当有一个明确的对象作为锁时,就能够这样写程式,但当没有明确的对象作为锁,只是想让一段代码同步时,能够创建一个特别的instance变量(它得是个对象)来充当锁:
class Foo implements Runnable{ private byte[] lock = new byte[0]; // 特别的instance变量 public void method(){ synchronized(lock) { //… } } //….. }
注:零长度的byte数组对象创建起来将比任何对象都经济。查看编译后的字节码:生成零长度的byte[]对象只需3条操作码,而Object lock = new Object()则需要7行操作码。
3.将synchronized作用于static 函数,示例代码如下:
Class Foo{ public synchronized static void method1() // 同步的static 函数{ //…. }
public void method2(){ synchronized(Foo.class) // class literal(类名称字面常量){ } }
代码中的method2()方法是把class literal作为锁的情况,他和同步的static函数产生的效果是相同的,取得的锁很特别,是当前调用这个方法的对象所属的类(Class类,而不再是由这个Class产生的某个具体对象了)。
转载自:http://www.strutshome.com/index.php/archives/495
详细参考:http://www.cnblogs.com/GnagWang/archive/2011/02/27/1966606.html
一句话概括:两种锁对象;一种是实例对象,一种是class对象,分别对应动态和静态
总结:
需要注意的是同步加锁的是对象而非代码,如果在一个类中有一个同步方法,该方法是可以被两个不同的线程同时执行的。只要每一个线程自己创建一个该类的实例即可。
synchronized关键字锁的是对象而不是代码块,如果需要实现真正的同步,必须同步一个全局对象或者对类进行同步。通过对当前类进行加锁,达到了同步的效果,但是基于线程同步的一般原理是应该尽量减少同步的粒度以达到更高的性能。