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 脑图: