概述
本文简易介绍下读写锁的策略
1.在公平锁的条件下,所有的锁都不允许插队
2.在非公平的条件下:
写锁是可以插队的(写锁插队可以避免饥饿)
读锁仅在等待队列头结点不是想获取写锁的线程的时候是可以插队的。
接下来我会从ReentrantReadWriteLock源码部分进行说明。
公平锁的条件下读写锁的策略
我们从ReentrantReadWriteLock 的FairLock中查看策略,如下图所示。
从红圈是可以看到读锁和写锁的阻塞条件都是当队列中有等待的处理任务。即就是读锁和写锁是不可以插队的。
非公平锁的情况下的读写策略
下面我们介绍非公平锁的情况下,写锁是可以插队,读锁只有在头结点不是写线程的情况是可以插队,见下图所示。
读写锁测试实例代码
下文从实例代码来说明读写的插队策略问题
代码是实现结果为:非公平锁的条件下,线程1读,线程2读,线程3写,线程4读,执行结果为线程4在线程3之后
非公平条件下读锁插队代码
下文代码演示非公平的条件下,读锁进行插队的实现,见下代码。
非公平条件下,读锁插队的条件是:当头结点不是写锁时,可以插队。
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* 本实例演示非公平的条件下,读锁插队的问题(当头结点不是写线程的时候)
*/
public class NoFairReadWriteDemo {
private static ReentrantReadWriteLock reentrantReadWriteLock=new ReentrantReadWriteLock(false);
private static ReentrantReadWriteLock.ReadLock readLock=reentrantReadWriteLock.readLock();
private static ReentrantReadWriteLock.WriteLock writeLock=reentrantReadWriteLock.writeLock();
public void read(){
System.out.println(Thread.currentThread().getName()+"尝试获取读锁");
readLock.lock();
try{
System.out.println(Thread.currentThread().getName()+"获取到了读锁");
}finally {
readLock.unlock();
}
}
public void write(){
System.out.println(Thread.currentThread().getName()+"尝试获取写锁");
writeLock.lock();
try{
Thread.sleep(20);
System.out.println(Thread.currentThread().getName()+"获取到了写锁");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
writeLock.unlock();
}
}
/**
* 线程1获取写锁,其他线程尝试获取
* 线程1释放后,队列头为线程2,进行读;线程3也进行读,测试:线程2在读取的时候,子线程能否插队成功
* @param args
*/
public static void main(String[] args) {
NoFairReadWriteDemo noFairReadWriteDemo=new NoFairReadWriteDemo();
new Thread(()->noFairReadWriteDemo.write(),"线程1").start();
new Thread(()->noFairReadWriteDemo.read(),"线程2").start();
new Thread(()->noFairReadWriteDemo.read(),"线程3").start();
new Thread(()->noFairReadWriteDemo.write(),"线程4").start();
new Thread(()->noFairReadWriteDemo.read(),"线程6").start();
new Thread(() -> {
Thread[] threads=new Thread[1000];
for (int i = 0; i < 1000; i++) {
int finalI = i;
threads[i]=new Thread(()->noFairReadWriteDemo.read(),"子线程"+ finalI);
}
for (int i = 0; i < 1000; i++) {
threads[i].start();
}
}, "线程6").start();
}
}