1 关于读写锁的一些特点:
a) 读写锁是JVM自己控制的,我们只要上相应方法上加锁即可
b) 多个读锁共享数据/操作
c) 读锁和写锁行为互斥
d) 写锁和写锁行为互斥
2 读写锁应用案例1,
package thread; import java.util.Random; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * 多线程下 * 写的时候 独占 * 读的时候 共享 分别在对应的方法上 增加 读/写锁,剩下关于独占和共享功能交给jdk5来完成。 * @author zm * */ public class ReadWriteLockExamp { /** * 如下数据说明: 线程在读的时候 没有写行为 同样 线程写时没有读行为 Thread-0 be ready to read data! Thread-0 read the data is 0 Thread-1 be ready to write data! Thread-1 write the data is 1667 Thread-2 be ready to read data! Thread-2 read the data is 1667 Thread-3 be ready to write data! Thread-3 write the data is 6691 Thread-4 be ready to read data! Thread-4 read the data is 6691 Thread-5 be ready to write data! Thread-5 write the data is 6950 Thread-6 be ready to read data! Thread-8 be ready to read data! Thread-6 read the data is 6950 Thread-8 read the data is 6950 Thread-7 be ready to write data! Thread-7 write the data is 1142 Thread-9 be ready to write data! Thread-9 write the data is 331 */ public static void main(String[] args) { final Quene quene = new Quene(); for(int i=0; i<5; i++){ new Thread(new Runnable() { @Override public void run() { quene.readData(); } }).start(); new Thread(new Runnable() { @Override public void run() { quene.writeData(); } }).start(); } } } class Quene { private int data; ReadWriteLock lock = new ReentrantReadWriteLock(); public void readData(){ // 增加读锁 大家都在读数据 为了防止读的过程中别人同时写东西,因此需要加入读锁 try { lock.readLock().lock(); System.out.println(Thread.currentThread().getName() + " be ready to read data!"); Thread.sleep(10); System.out.println(Thread.currentThread().getName() + " read the data is " + data); } catch (Exception e) { e.printStackTrace(); }finally{ lock.readLock().unlock();// 及时关闭 } } public void writeData(){ // 增加写锁 try { lock.writeLock().lock(); System.out.println(Thread.currentThread().getName() + " be ready to write data!"); Thread.sleep(100); data = new Random().nextInt(10000); System.out.println(Thread.currentThread().getName() + " write the data is " + data); } catch (Exception e) { e.printStackTrace(); }finally{ lock.writeLock().unlock(); // 及时关闭 } } }
3 利用读写锁模拟一个缓存系统,
写法参考 API下类 ReentrantReadWriteLock 下 class CachedData {...} 细节自行查找
package thread; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; public class MyCache { /** * */ public static void main(String[] args) { } } class CacheData { String value = ""; ReadWriteLock lock = new ReentrantReadWriteLock(); public String processCacheData(){ lock.readLock().lock(); try{ if("".equals(value)){// 找不到数据 lock.readLock().unlock();// 放掉读锁,带上写锁(因为读写锁互斥) lock.writeLock().lock();// 当最早的线程加写锁后,其余稍后的并发过来的读线程被互斥等待,直到最先的线程执行到 释放写锁后,才能继续执行后续代码 try{ if("".equals(value)){// 这里必须在又一次非空判断,防止 第一次时并发过来时 重复执行赋值操作 value = "hello hadoop"; // 真实代码为业务逻辑 eg: get user from db } }finally{ lock.writeLock().unlock(); // 写完数据后放弃写锁 } lock.readLock().lock();// 增加读锁,防止稍后线程获取到写锁,执行重写写操作 } }finally{ lock.readLock().unlock(); // 为了防止业务层出现异常,因此所有的释放锁操作都要放在finally执行 } return value; } }
4 读写锁适应的环境:
以下从jdk ReadWriteLock api下部分摘抄:
与互斥锁相比,使用读-写锁所允许的并发性增强将带来更大的性能提高
在实践中,只有在多处理器上并且只在访问模式适用于共享数据时,才能完全实现并发性增强。
如果数据更新变得频繁,数据在大部分时间都被独占锁,这时,就算存在并发性增强,也是微不足道的
如果读取操作所用时间太短,则读-写锁实现(它本身就比互斥锁复杂)的开销将成为主要的执行成本
只有通过分析和测量,才能确定应用程序是否适合使用读-写锁。
总结就是: 仅仅适合于 大量读, 少量写的场合
5 脑图: