最近在看<<Java并发编程实践>>,有这样一个类:ReentrantReadWriteLock。在这里做一个小结:
线程获得写锁的前提条件:
其他线程没有获得读锁:注意一定是其他线程!!!!!!!!!!!!!!!
public class ReadWriteLock{
static ReentrantReadWriteLock myLock=new ReentrantReadWriteLock();
static Lock readLock=myLock.readLock();
static Lock writeLock=myLock.writeLock();
static class MyThread extends Thread{
public void run() {
readLock.lock();
//readLock.unlock();
}
}
public static void main(String args[]){
new MyThread().start();
writeLock.lock(); //通过debug,发现程序阻塞在这里了,发生死锁。下面的"here"打印不出来。
System.out.println("here");
}
}
//运行结果:
//程序进入死锁,writeLock一直等待。
//只要将readLock.unlock()的注释解开,程序就跑通了。
//可见:要想获得写锁,别的线程必须没有获得读锁。
其他线程没有获得写锁:
public class ReadWriteLock{
static ReentrantReadWriteLock myLock=new ReentrantReadWriteLock();
static Lock readLock=myLock.readLock();
static Lock writeLock=myLock.writeLock();
static class MyThread extends Thread{
public void run() {
writeLock.lock();
//writeLock.unlock();
}
}
public static void main(String args[]){
new MyThread().start();
writeLock.lock(); //通过debug,发现程序阻塞在这里了,发生死锁。下面的"here"打印不出来。
System.out.println("here");
}
}
//运行结果:
//效果同上,发生死锁.若解开writeLock.unlock()的注释则程序正常,成功输出here
线程进入读锁的前提条件:
其他线程没有获得写锁:
public class ReadWriteLock{
static ReentrantReadWriteLock myLock=new ReentrantReadWriteLock();
static Lock readLock=myLock.readLock();
static Lock writeLock=myLock.writeLock();
static class MyThread extends Thread{
public void run() {
writeLock.lock();
//writeLock.unlock();
}
}
public static void main(String args[]){
new MyThread().start();
readLock.lock(); //通过debug,发现程序阻塞在这里了,发生死锁。下面的"here"打印不出来。
System.out.println("here");
}
}
//结果同上,获得读锁的前提是没有别的线程获得写锁
没有写请求或者有写请求,但调用线程和持有锁的线程是同一个:
这一条完全不明白是什么意思。
*******************************************************************************
还有一个特性:一个线程可以先获得写锁,再获得读锁,比如这样:
public class ReadWriteLock{
static ReentrantReadWriteLock myLock=new ReentrantReadWriteLock();
static Lock readLock=myLock.readLock();
static Lock writeLock=myLock.writeLock();
public static void main(String args[]){
writeLock.lock();
readLock.lock();
System.out.println("here");
}
}
//运行结果:程序正常
here
但是:一个线程不能先获得读锁再获得写锁,像这样:
public class ReadWriteLock{
static ReentrantReadWriteLock myLock=new ReentrantReadWriteLock();
static Lock readLock=myLock.readLock();
static Lock writeLock=myLock.writeLock();
public static void main(String args[]){
readLock.lock();
writeLock.lock();
System.out.println("here");
}
}
//运行结果:
//程序进入死循环
因为一个线程可以先获得写锁,再获得读锁。所以:可以实现降级锁,即: 从写入锁降级为读取锁,其实现方式是:先获取写入锁,然后获取读取锁,最后释放写入锁。像这样:
public class ReadWriteLock{
static ReentrantReadWriteLock myLock=new ReentrantReadWriteLock();
static Lock readLock=myLock.readLock();
static Lock writeLock=myLock.writeLock();
public static void main(String args[]){
writeLock.lock();
readLock.lock();
writeLock.unlock();
System.out.println("here");
}
}
//运行结果:
here
但是,从读取锁升级到写入锁是不可能的。
最后一点:ReadLock可以被多个线程持有并且在作用时排斥任何的WriteLock,而WriteLock则是完全的互斥。这一特性最为重要,因为对于高读取频率而相对较低写入的数据结构,使用此类锁同步机制则可以提高并发量。
下面根据一个网上的例子说明这一点:
public class ReentrantReadWriteLockSample {
public static void main(String[] args) {
testReadLock();
// testWriteLock();
}
public static void testReadLock() {
final ReadWriteLockSampleSupport support = new ReadWriteLockSampleSupport();
Runnable runnable = new Runnable() {
public void run() {
support.get("test");
}
};
new Thread(runnable).start();
new Thread(runnable).start();
new Thread(new Runnable() {
public void run() {
support.put("test", "test");
}
}).start();
}
public static void testWriteLock() {
final ReadWriteLockSampleSupport support = new ReadWriteLockSampleSupport();
new Thread(new Runnable() {
public void run() {
support.put("key1", "value1");
}
}).start();
new Thread(new Runnable() {
public void run() {
support.put("key2", "value2");
}
}).start();
new Thread(new Runnable() {
public void run() {
support.get("key1");
}
}).start();
}
}
class ReadWriteLockSampleSupport {
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock readLock = lock.readLock();
private final Lock writeLock = lock.writeLock();
private volatile boolean completed= true;
private Map<String,String> cache = new HashMap<String,String>(32);
public String get(String key) {
readLock.lock();
System.out.println(Thread.currentThread().getName() + " read.");
startTheCountdown(); //等待5s
try{
return cache.get(key);
}
finally{
readLock.unlock();
}
}
public String put(String key, String value) {
writeLock.lock();
System.out.println(Thread.currentThread().getName() + " write.");
startTheCountdown();
try{
return cache.put(key, value);
}
finally {
writeLock.unlock();
}
}
/**
* A simple countdown,it will stop after about 5s.
*/
public void startTheCountdown() {
long currentTime = System.currentTimeMillis();
for(;;) {
long diff = System.currentTimeMillis() - currentTime;
if(diff > 5000) {
break;
}
}
}
}
// testReadLock(); 和 testWriteLock(); 分开运行
ReentrantReadWriteLockSample中的两个静态测试方法则分别测试了ReadLock和WriteLock的排斥性。 testReadLock()中,开启三个线程,前两者试图获取ReadLock而后者去获取WriteLock。执行结果可以看 到:ReadWriteLockSampleSupport的get()方法中的打印结果在前两个线程中几乎同时显示,而put()中的打印结果则要等上 近5s。这就说明了,ReadLock可以多线程持有并且排斥WriteLock的持有线程。testWriteLock()中,也开启三个线程。前两个 是去获取WriteLock,最后一个获取ReadLock。执行的结果是三个打印结果都有近5s的间隔时间,这说明了WriteLock是独占的。