1.声明synchronized方法的方式,这个称之为方法锁或者对象锁。
synchronized void f(){/*....*/}
synchronized void g(){/*....*/}
所有的对象都自动含有单一的锁(也称为监视器,前面阅读Thread源码有提到监视器)。当在对象上调用任意synchronized方法时,此对象都被加锁,这时该对象上的其他synchronized方法只有等到前一个方法调用完毕释放了锁之后才能被调用。对于前面的方法,如果某个任务对对象调用了f(),对于同一个对象而言,只有等到f()调用结束并释放了锁之后,其他任务才能调用f()或者g()。所以,对于某个特定的对象来说,其所有的synchronized方法共享同一个锁,这可以被用来防止多个任务同时访问被编码对象内存。
一个任务可以多次获得对象锁,也就是锁可重入。是指,一个方法在同一个对象上调用了第二个方法,后者又调用了同一个对象上的另外一个方法,就是发生锁的重入。JVM负责跟踪对象被枷锁的次数。如果一个对象被解锁也就说锁被完全释放,其计数变为0。在任务第一次给对象加锁时,计数变为1。每当这个相同的任务在这个对象上获得锁的时候,计数都会递增。显然,只有先获得了锁的任务才能允许继续获得锁。每当任务离开一个synchronized方法,计数递减,当计数为0的时候,锁被完全释放,此时别的任务就可以继续使用此资源。
2.类锁,可以看做特殊的对象锁,锁的是类对象
针对每个类,也有一个锁,作为Class对象的一部分,所以synchronized static方法可以在类的范围内防止对static数据的并发访问。
3.显式使用Lock对象
基本上,当你使用关键字synchronized关键字时,需要的代码量更少,并且用户出现错误的概率更低,因此通常只有解决特殊任务时才会使用到现实的Lock对象。比如,用synchronized不能尝试获取锁并且最终会获取锁失败,或者尝试着获取锁等待一段时间,这些需要concurrent类库
abstract class IntGenerator{
private volatile boolean canceled = false;
public abstract int next();
public void cancel(){this.canceled =true;}
public boolean isCanceled(){return this.canceled;}
}
class EvenChecker implements Runnable{
private IntGenerator generator;
private final int id;
public EvenChecker(IntGenerator g,int ident){
generator = g;
id = ident;
}
public void run() {
while (!generator.isCanceled()){
int val = generator.next();
if(val%2 != 0){
System.out.println(val + " not even!");
generator.cancel();
}
}
}
public static void test(IntGenerator gp,int count){
System.out.println("Press Contrl-C to exit");
ExecutorService exec = Executors.newCachedThreadPool();
for(int i=0;i<count;i++){
exec.execute(new EvenChecker(gp,i));
}
exec.shutdown();
}
public static void test(IntGenerator gp){
test(gp,10);
}
}
class SynchronizedEvenGenerator extends IntGenerator{
private int currentVlaue = 0;
public synchronized int next() {
++currentVlaue;
//当不使用synchronized修饰next方法时,调用该方法可以促使线程同步调用异常发生
//Thread.yield();
//同样说明一个问题Thread。yield即便是让出cpu但是并没有释放锁
++currentVlaue;
return currentVlaue;
}
}
class MutexEvenGenerator extends IntGenerator{
private int currentVlaue = 0;
private Lock lock = new ReentrantLock();
public int next() {
lock.lock();
try{
++currentVlaue;
//当不使用synchronized修饰next方法时,调用该方法可以促使线程同步调用异常发生
Thread.yield();
//同样说明一个问题Thread。yield即便是让出cpu但是并没有释放锁
++currentVlaue;
return currentVlaue;
}finally {
lock.unlock();
}
}
}
public class NesttyMain implements Serializable{
public static void main(String[] args){
// EvenChecker.test(new SynchronizedEvenGenerator());
EvenChecker.test(new MutexEvenGenerator());
}
}