最近在看<<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是独占的。