package com.zc.study;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* @author zc
* 读写锁Demo
* ReentrantReadWriteLock读写锁定义:
* 1、支持一写多读的同步锁(一把写锁,多把读锁),读写分离,可分别配置读锁和写锁
* 2、支持分配多个读锁,可以使多个读操作并发执行
* 注意:读锁和写锁必须来自于同一个ReentrantReadWriteLock对象,这样读写锁才是互斥的
*
* 互斥规则:
* a、写锁 与 写锁 :互斥,阻塞
* b、读锁 与 写锁 :互斥,读锁阻塞写锁,写锁阻塞读锁
* c、读锁 与 读锁 : 不互斥,不阻塞;多把读锁可以并发执行
*
* 使用场景:在读操作远高于写操作的场景下,可在保证线程安全的情况下大幅提升效率
*/
public class ReentrantReadWriteLockDemo {
/**创建读写锁对象*/
ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
/**从读写锁对象中获取读锁*/
ReentrantReadWriteLock.ReadLock rLock = lock.readLock();
/**从读写锁对象获取写锁*/
ReentrantReadWriteLock.WriteLock wLock = lock.writeLock();
/**实例化一个对象*/
final static ReentrantReadWriteLockDemo demo = new ReentrantReadWriteLockDemo();
public static void main(String[] args) {
//读操作
Callable<Integer> write = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
demo.setValue(11);
return null;
}
};
//写操作
Callable<Integer> read = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
demo.getValue();
return null;
}
};
//实例化线程池对象,创建20个线程
ExecutorService es = Executors.newFixedThreadPool(20);
//开始时间
long start = System.currentTimeMillis();
//分配给写操作2个线程
for (int i = 0; i < 2; i++) {
es.submit(write);
}
//分配给读操作18个线程
for (int i = 0; i < 18; i++) {
es.submit(read);
}
es.shutdown();//运行完任务后停止接受新任务
//判断执行完shutdown命令后,是否运行完毕,如果没有就一直判断,直到所有线程运行完毕为止
while (true){
if(es.isTerminated()) {
break;
}
}
//结束时间
long end = System.currentTimeMillis();
//完成时间
System.out.println(end - start);
}
private int value;
/**读操作*/
public int getValue() {
rLock.lock();//拿到读锁
try {
try{
//每次睡眠1s
Thread.sleep(1000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
return value;
}finally{
rLock.unlock();//释放读锁
}
}
/**写操作*/
public void setValue(int value) {
wLock.lock();//获取写锁
try {
try{
//每次睡眠1s
Thread.sleep(1000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
this.value = value;
}finally{
wLock.unlock();//释放写锁
}
}
}
测试结果是执行完毕所有代码用时 3s 左右,而我们一共有20个线程同时并发执行,每个写操作和读操作都会睡眠 1s ,而写锁有 2 把,读锁有 18 把,如果读锁和读锁是相互排斥的,那么我们的运行时间应该是 20s 以上,由此可以看到除了 2 把写锁分别耗时 1s 外,所有的读锁同时并发执行,也就是在 1s 内,18个读线程同时并发执行完毕。因此当读多写少,使用读写锁既能保证线程安全,还能提升效率。