7、ReadWriteLock
读写锁:分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,写锁与写锁互斥,这是由JVM控制的,我们只要上好相应的锁即可。
如果你的代码只读数据,可以很多人同时读,但是不能同时写,那就上读锁;如果代码修改数据,只能一个人在写,且不能同时读取,
那就上写锁。总之,读的时候上读锁,写的时候上写锁。
java.util.concurrent.lock
Interface ReadWriteLock
实现类:
ReentrantReadWriteLock
ReadWriteLock实现了两把锁,一把只能进行读操作,一把进行写操作。读锁能够被多个线程拥有,写锁具有排他性。
所有ReadWriteLock的实现必须保证写锁操作对于读锁操作内存的同步(当然拥有写锁时具有排他性,此时只要保证写操作的内存对读操作
保持可见性就可以了,当然也就是所谓的同步了)。通俗点说,一个成功获得读锁的线程能够看到所有先前释放的写锁的更新。
读写锁比相互排它锁支持更大的并发量。它利用的一个事实是:同一时间只能有一个线程对共享数据进行更新,但是可以有多个线程对它进行读取。
读写锁的实现当然要比互斥锁的实现更难。此时,我们的应用到底是选择互斥锁还是读写锁,取决于我们应用读写操作发生的频率。如果读操作是频繁
发生的,而写操作不那么频繁,那么可以选择读写锁,这样会提高应用的性能;反之,如果我们的应用写操作比较频繁而读操作不那么频繁,并且读操作
是一些短时间的操作,那么就没有必要用到读写锁。
利用ReentrantReadWriteLock实现缓存:
1 class CachedData { 2 Object data; 3 volatile boolean cacheValid; 4 ReentrantReadWriteLock rwl=new ReentrantReadWriteLock(); 5 6 void processCachedData() { 7 rwl.readLock().lock(); 8 if(!cacheValid) { 9 //在获得写锁之前必须释放读锁 10 rwl.readLock().unlock(); 11 rwl.writeLock().lock(); 12 13 //再次检测状态,因为其他线程有可能获得 14 //写锁并且在我们操作之前改变了状态 15 if(!cacheValid) { 16 //data=....; 17 cacheValid=true; 18 } 19 //在释放写锁之前通过获得读锁进行降级 20 rwl.readLock().lock(); 21 rwl.writeLock().unlock();//释放写锁仍然拥有读锁 22 } 23 use(data); 24 rwl.readLock().unlock(); 25 } 26 }
利用ReentrantReadWriteLock提供集合的并发性能:
1 class RWDictionary { 2 private final Map<String,Data> m=new TreeMap<String,Data>(); 3 private final ReentrantReadWriteLock rwl=new ReentrantReadWriteLock(); 4 private final Lock r=rwl.readLock(); 5 private final Lock w=rwl.writeLock(); 6 7 public Data get(String key) { 8 r.lock(); 9 try { 10 return m.get(key); 11 }finally { 12 r.unlock(); 13 } 14 } 15 16 public String[] allKeys() { 17 r.lock(); 18 try { 19 return (String[])m.keySet().toArray(); 20 }finally { 21 r.unlock(); 22 } 23 } 24 25 public Data put(String key,Data value) { 26 w.lock(); 27 try { 28 return m.put(key, value); 29 }finally { 30 w.unlock(); 31 } 32 } 33 34 public void clear() { 35 w.lock(); 36 try { 37 m.clear(); 38 }finally { 39 w.unlock(); 40 } 41 } 42 }