Java?5?引入的?Concurrent?并发库软件包中,提供了?ReentrantLock?可重入同步锁,用来替代?synchronized?关键字原语,并可提供更好的性能,以及更强大的功能。使用方法也很简单:
public?final?ReentrantLock?lock=new?ReentrantLock();
......
try?{
? ? lock.lock();
? ? //?进入同步内容
? ? ....
}?finally?{
? ? lock.unlock();?//?必须在?finally?块中解锁,否则一旦出现异常,执行不到解锁,则一直锁住了。
}
?
synchronized原语和ReentrantLock在一般情况下没有什么区别,但是在非常复杂的同步应用中,请考虑使用ReentrantLock,特别是遇到下面2种需求的时候。?
1.某个线程在等待一个锁的控制权的这段时间需要中断?
2.需要分开处理一些wait-notify,ReentrantLock里面的Condition应用,能够控制notify哪个线程?
3.具有公平锁功能,每个到来的线程都将排队等候?
先说第一种情况,ReentrantLock的lock机制有2种,忽略中断锁和响应中断锁,这给我们带来了很大的灵活性。比如:如果A、B2个线程去竞争锁,A线程得到了锁,B线程等待,但是A线程这个时候实在有太多事情要处理,就是一直不返回,B线程可能就会等不及了,想中断自己,不再等待这个锁了,转而处理其他事情。这个时候ReentrantLock就提供了2种机制,第一,B线程中断自己(或者别的线程中断它),但是ReentrantLock不去响应,继续让B线程等待,你再怎么中断,我全当耳边风(synchronized原语就是如此);第二,B线程中断自己(或者别的线程中断它),ReentrantLock处理了这个中断,并且不再等待这个锁的到来,完全放弃。(如果你没有了解java的中断机制,请参考下相关资料,再回头看这篇文章,80%的人根本没有真正理解什么是java的中断,呵呵)?
这里来做个试验,首先搞一个Buffer类,它有读操作和写操作,为了不读到脏数据,写和读都需要加锁,我们先用synchronized原语来加锁,如下:?
public?class?Buffer?{? ?
? ? ?
? ? private?Object?lock;? ?
? ? ?
? ? public?Buffer()?{? ?
? ? ? ? lock?=?this;? ?
? ? }? ?
? ? ?
? ? public?void?write()?{? ?
? ? ? ? synchronized?(lock)?{? ?
? ? ? ? ? ? long?startTime?=?System.currentTimeMillis();? ?
? ? ? ? ? ? System.out.println("开始往这个buff写入数据…");? ?
? ? ? ? ? ? for?(;;)//?模拟要处理很长时间? ?
? ? ? ? ? ? {? ?
? ? ? ? ? ? ? ? if?(System.currentTimeMillis()? ?
? ? ? ? ? ? ? ? ? ? ? ? -?startTime?>?Integer.MAX_VALUE)? ?
? ? ? ? ? ? ? ? ? ? break;? ?
? ? ? ? ? ? }? ?
? ? ? ? ? ? System.out.println("终于写完了");? ?
? ? ? ? }? ?
? ? }? ?
? ? ?
? ? public?void?read()?{? ?
? ? ? ? synchronized?(lock)?{? ?
? ? ? ? ? ? System.out.println("从这个buff读数据");? ?
? ? ? ? }? ?
? ? }? ?
}? ?
接着,我们来定义2个线程,一个线程去写,一个线程去读。?
public?class?Writer?extends?Thread?{? ?
? ? ?
? ? private?Buffer?buff;? ?
? ? ?
? ? public?Writer(Buffer?buff)?{? ?
? ? ? ? this.buff?=?buff;? ?
? ? }? ?
? ? ?
? ? @Override? ?
? ? public?void?run()?{? ?
? ? ? ? buff.write();? ?
? ? }? ?
? ? ?
}? ?
? ? ?
public?class?Reader?extends?Thread?{? ?
? ? ?
? ? private?Buffer?buff;? ?
? ? ?
? ? public?Reader(Buffer?buff)?{? ?
? ? ? ? this.buff?=?buff;? ?
? ? }? ?
? ? ?
? ? @Override? ?
? ? public?void?run()?{? ?
? ? ?
? ? ? ? buff.read();//这里估计会一直阻塞? ?
? ? ?
? ? ? ? System.out.println("读结束");? ?
? ? ?
? ? }? ?
? ? ?
}? ?
好了,写一个Main来试验下,我们有意先去“写”,然后让“读”等待,“写”的时间是无穷的,就看“读”能不能放弃了。?
public?class?Test?{? ?
? ? public?static?void?main(String[]?args)?{? ?
? ? ? ? Buffer?buff?=?new?Buffer();? ?
? ? ?
? ? ? ? final?Writer?writer?=?new?Writer(buff);? ?
? ? ? ? final?Reader?reader?=?new?Reader(buff);? ?
? ? ?
? ? ? ? writer.start();? ?
? ? ? ? reader.start();? ?
? ? ?
? ? ? ? new?Thread(new?Runnable()?{? ?
? ? ?
? ? ? ? ? ? @Override? ?
? ? ? ? ? ? public?void?run()?{? ?
? ? ? ? ? ? ? ? long?start?=?System.currentTimeMillis();? ?
? ? ? ? ? ? ? ? for?(;;)?{? ?
? ? ? ? ? ? ? ? ? ? //等5秒钟去中断读? ?
? ? ? ? ? ? ? ? ? ? if?(System.currentTimeMillis()? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? -?start?>?5000)?{? ?
? ? ? ? ? ? ? ? ? ? ? ? System.out.println("不等了,尝试中断");? ?
? ? ? ? ? ? ? ? ? ? ? ? reader.interrupt();? ?
? ? ? ? ? ? ? ? ? ? ? ? break;? ?
? ? ? ? ? ? ? ? ? ? }? ?
? ? ?
? ? ? ? ? ? ? ? }? ?
? ? ?
? ? ? ? ? ? }? ?
? ? ? ? }).start();? ?
? ? ?
? ? }? ?
}? ?
我们期待“读”这个线程能退出等待锁,可是事与愿违,一旦读这个线程发现自己得不到锁,就一直开始等待了,就算它等死,也得不到锁,因为写线程要21亿秒才能完成?T_T?,即使我们中断它,它都不来响应下,看来真的要等死了。这个时候,ReentrantLock给了一种机制让我们来响应中断,让“读”能伸能屈,勇敢放弃对这个锁的等待。我们来改写Buffer这个类,就叫BufferInterruptibly吧,可中断缓存。?
import?java.util.concurrent.locks.ReentrantLock;? ?
? ? ?
public?class?BufferInterruptibly?{? ?
? ? ?
? ? private?Reentra