Java关键字synchronized是Java 语言提供的对多线程和同步的一种机制。synchronized可以作为函数的修饰符,也可作为函数内的语句。它可以作用于instance变量,对象引用(object reference),static函数和类名称字面常量(class literals)。

  下面介绍一下synchronized的关键字的使用方法:

  一、synchronized作函数修饰符

public synchronized void fun()
{
    ……
}

  fun()是一个同步方法,此时synchronized关键字锁定的是调用这个同步方法的对象。假设有p1和p2是同一个类的两个对象,p1在不同的线程中运行会对fun()产生互斥和同步的效果;但是p2对象和p1对象互相不会对fun()产生同步和互斥作用(当然,不同线程中的p2对象的fun()还是有同步和互斥)。

  对于非static的情况,synchronized是对象级别的,其实质是将synchronized作用于对象引用(object reference)上,即拿到p1对象锁的线程,对p1的fun()方法有同步互斥作用,不同的对象之间坚持“和平共处”。因此,上面的代码等同于:

public void fun()
{
  synchronized(this)  // this指的是调用这个方法的对象
  {
      ……
  }
}

  二、synchronized同步程序块

public void fun_1(someObject obj)
{
  synchronized(obj)
  {
      ……
  }
}

  上面代码中,锁住的是obj对象(正如前面说的,对于非static的情况,sysnchronized是对象级别的),谁拿到这个锁,谁可运行obj控制的那段代码。通常情况,如果我们知道对用哪个对象作为锁时,可以像上面的代码块一样使用synchronized。假如没有明确的对象作为锁,程序员又希望同步一段代码块,可以使用下面的trick。

class Test implements Runnable
{
  private byte[] lock = new byte[0];  // 定一个instance变量
  public void fun_2()
  {
      synchronized(lock)
      {
          ……
      }
  }
}

  在上面的代码示例中,定义了一个特殊的instance变量作为锁,这个instance变量必须是一个对象。定义lock为长度为0的数组对象是佳方案。在编译后的字节码中,生成长度为0的byte[]只需要三条操作码。假如我们用所有类的超类Object来作锁,需要生成七条操作码。

  [注意:]如果需要定义特殊的instance变量作为锁,好将其定义为private的,同时定义其get()方法(如果使用自己定义的类的对象作为锁)。如果变量是public的,其他类的对象可以得到这个锁的控制权,并修改这个锁。这是非常不安全的。

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