概念
- 共享锁:允许多个线程共同持有一把锁,又称读锁,多个线程可以同时获取读锁。
- 排它锁:只允许一个线程持有锁,又称写锁,只有一个线程可以获取写锁。
读写锁规则总结:
读写锁只是一把锁,只是有两种锁定方式
。要么有一个线程获取写锁,要么有一个或多个线程获取读锁,两个不会同时出现(要么多读,要么一写)。
读写锁的插队策略
- 如果是公平锁,读、写锁都不允许插队,如果前面有等待线程,直接进行排队。
- 如果是非公平锁。
- 写锁可以随时插队尝试,拿不到锁进行排队。
- 读锁在等待线程队列头结点不是写锁时可以插队,否则直接进行排队。
非公平锁插队例子说明:
线程1、线程2获取读锁在执行,线程3获取写锁失败在排队,
- 情况1:这时线程4获取写锁,尝试加锁,失败后进行排队。
- 情况2:这时线程4获取读锁,因为头结点等待线程为写锁,所以直接进行排队。
读写锁的插队策略源码解析
调用的都是是否阻塞的方法,true就直接排队,false就可以尝试插队。
公平锁情况
如果是公平锁,不允许插队,如果前面有等待线程,直接进行排队
。
public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializable {
static final class FairSync extends Sync {
// 写锁是否阻塞的方法
final boolean writerShouldBlock() {
// 看前面是否有等待线程,有就true阻塞排队,没有就false不需要阻塞排队
return hasQueuedPredecessors();
}
// 读锁是否阻塞的方法
final boolean readerShouldBlock() {
// 看前面是否有等待线程,有就true阻塞排队,没有就false不需要阻塞排队
return hasQueuedPredecessors();
}
}
}
非公平锁情况
非公平锁的写锁可以随时插队尝试,拿不到锁进行排队
。
读锁在等待线程队列头结点不是写锁时可以插队,否则直接进行排队。
public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializable {
static final class NonfairSync extends Sync {
// 写锁是否阻塞的方法
final boolean writerShouldBlock() {
// 直接放回false,非公平锁写锁可以插队
return false; // writers can always barge
}
// 读锁是否阻塞的方法
final boolean readerShouldBlock() {
// 判断等待线程队列头结点不是写锁时可以插队,否则直接进行排队。
return apparentlyFirstQueuedIsExclusive();
}
}
}
示例代码:
读写锁基本使用
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* @author weilai
* @email 352342845@qq.com
* @date 2020/3/23 12:58 下午
*/
public class DemoLock {
private static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock(false);
private static ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
private static ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
public static void read() {
System.out.println(Thread.currentThread().getName() + "开始执行");
readLock.lock();
try {
System.out.println(Thread.currentThread().getName() + "拿到读锁");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} finally {
readLock.unlock();
System.out.println(Thread.currentThread().getName() + "释放读锁");
}
}
public static void write() {
System.out.println(Thread.currentThread().getName() + "开始执行");
writeLock.lock();
try {
System.out.println(Thread.currentThread().getName() + "拿到写锁");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}finally {
writeLock.unlock();
System.out.println(Thread.currentThread().getName() + "释放写锁");
}
}
public static void main(String[] args) {
new Thread(() -> write()).start();
new Thread(() -> read()).start();
new Thread(() -> read()).start();
new Thread(() -> write()).start();
new Thread(() -> write()).start();
new Thread(() -> read()).start();
}
}
output:
Thread-0开始执行
Thread-1开始执行
Thread-0拿到写锁
Thread-2开始执行
Thread-3开始执行
Thread-4开始执行
Thread-5开始执行
Thread-0释放写锁
Thread-1拿到读锁
Thread-2拿到读锁
Thread-1释放读锁
Thread-3拿到写锁
Thread-2释放读锁
Thread-3释放写锁
Thread-4拿到写锁
Thread-4释放写锁
Thread-5拿到读锁
Thread-5释放读锁
非公平读锁插队
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* @author weilai
* @email 352342845@qq.com
* @date 2020/3/23 12:58 下午
*/
public class DemoLock {
private static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock(false);
private static ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
private static ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
public static void read() {
System.out.println(Thread.currentThread().getName() + "尝试获取读锁");
readLock.lock();
try {
System.out.println(Thread.currentThread().getName() + "拿到读锁");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
} finally {
System.out.println(Thread.currentThread().getName() + "释放读锁");
readLock.unlock();
}
}
public static void write() {
System.out.println(Thread.currentThread().getName() + "尝试获取写锁");
writeLock.lock();
try {
System.out.println(Thread.currentThread().getName() + "拿到写锁");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}finally {
System.out.println(Thread.currentThread().getName() + "释放写锁");
writeLock.unlock();
}
}
public static void main(String[] args) {
new Thread(() -> write()).start();
new Thread(() -> read()).start();
new Thread(() -> read()).start();
new Thread(() -> write()).start();
new Thread(() -> read()).start();
new Thread(() -> {
Thread[] threads = new Thread[1000];
for (int i = 0; i < 1000; i++) {
threads[i] = new Thread(() -> read(), "子线程" + i);
threads[i].start();
}
}).start();
}
}
output:
读写锁的升降级
写锁的优先级高于读锁
,持有读锁再次获取写锁为升级,持有写锁再次获取读锁为降级。读写锁可以降级,不可以升级
。
代码演示可以降级不能升级
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* @author weilai
* @email 352342845@qq.com
* @date 2020/3/23 12:58 下午
*/
public class DemoLock {
private static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock(false);
private static ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
private static ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
public static void readUpWrite() {
readLock.lock();
try {
System.out.println("获取读锁");
writeLock.lock();
System.out.println("获取写锁");
} finally {
writeLock.unlock();
readLock.unlock();
}
}
public static void writeDownRead() {
writeLock.lock();
try {
System.out.println("获取写锁");
readLock.lock();
System.out.println("获取读锁");
} finally {
readLock.unlock();
writeLock.unlock();
}
}
public static void main(String[] args) {
Thread t1 = new Thread(() -> writeDownRead());
t1.start();
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("----------");
new Thread(() -> readUpWrite()).start();
}
}
out:升级会阻塞线程
获取写锁
获取读锁
----------
获取读锁