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产生的某个具体对象了)。
记得在《Effective Java》一书中看到过将 Foo.class和 P1.getClass()用于作同步锁还不一样,不能用P1.getClass()来达到锁这个Class的目的。P1指的是由Foo类产生的对象。
可以推断:如果一个类中定义了一个synchronized的static函数A,也定义了一个synchronized 的instance函数B,那么这个类的同一对象Obj在多线程中分别访问A和B两个方法时,不会构成同步,因为它们的锁都不一样。A方法的锁是Obj这个对象,而B的锁是Obj所属的那个Class。
小结如下:
搞清楚synchronized锁定的是哪个对象,就能帮助我们设计更安全的多线程程序。
还有一些技巧可以让我们对共享资源的同步访问更加安全:
1. 定义private 的instance变量+它的 get方法,而不要定义public/protected的instance变量。如果将变量定义为public,对象在外界可以绕过同步方法的控制而直接取得它,并改动它。这也是JavaBean的标准实现方式之一。
2. 如果instance变量是一个对象,如数组或ArrayList什么的,那上述方法仍然不安全,因为当外界对象通过get方法拿到这个instance 对象的引用后,又将其指向另一个对象,那么这个private变量也就变了,岂不是很危险。这个时候就需要将get方法也加上synchronized同步,并且,只返回这个private对象的clone()――这样,调用端得到的就是对象副本的引用了。
// 以下是我学习过程中自己写的注释和例子,希望对大家有帮助!不妥之处望指出
/**
* java同步实现的两种方法,都是使用synchronized关键字:
* 1:实现时就是放该关键字到实例方法或类方法前面,如:public synchronized void method (){ }
* 2:采用synchronized块,使用如下:synchronized (Expression) { Block } 其中Expression为对象的引用,
* 在进入同步块Block之前,必须在Expression上取得锁。如果已有其他线程取得了这把锁,块便不能进入,
* 必须等候那把锁被释放。----表示只对Expression引用的对象才锁定代码块Block,对其它对象不锁
* 在方法前作为修饰词和用到statement块的差别在于:前者是在运行时实现锁功能,后者在编译时生成的class文件中体现锁机制。
*
* synchronized修饰方法时,表示该方法是同步的,同一时刻只能有一个线程使用它。
*
* 下面列出简单的synchronized方法:
* synchronized void f()
* synchronized void g()
* 每个对象都包含了一把锁(也叫作“监视器”),它自动成为对象的一部分(不必为此写任何特殊的代码)。
* 调用任何synchronized方法时,对象就会被锁定,不可再调用那个对象的其他任何synchronized方法,
* 除非第一个方法完成了自己的工作,并解除锁定。在上面的例子中,如果为一个对象调用f(),便不能再为
* 同样的对象调用g(),除非f()完成并解除锁定。因此,一个特定对象的所有synchronized方法都共享着一把锁,
* 而且这把锁能防止多个方法对通用内存同时进行写操作(比如同时有多个线程)。
* 每个类也有自己的一把锁(作为类的Class对象的一部分),所以synchronized static方法可在一个类的
* 范围内被相互间锁定起来,防止与static数据的接触。
*