大型网站怎么做,淘宝客怎么在微博做网站,建筑公司网站领导致辞,婚庆公司赚钱吗一、为什么要出现读写锁
synchronized和ReentrantLock都是互斥锁。 如果说有一个操作是读多写少的#xff0c;还要保证线程安全的话。如果采用上述的两种互斥锁#xff0c;效率方面很定是很低的。 在这种情况下#xff0c;咱们就可以使用ReentrantReadWriteLock读写锁去实现…一、为什么要出现读写锁
synchronized和ReentrantLock都是互斥锁。 如果说有一个操作是读多写少的还要保证线程安全的话。如果采用上述的两种互斥锁效率方面很定是很低的。 在这种情况下咱们就可以使用ReentrantReadWriteLock读写锁去实现。 读读之间是不互斥的可以读和读操作并发执行。 但是如果涉及到了写操作那么还得是互斥的操作。
static ReentrantReadWriteLock lock new ReentrantReadWriteLock();
static ReentrantReadWriteLock.WriteLock writeLock lock.writeLock();
static ReentrantReadWriteLock.ReadLock readLock lock.readLock();
public static void main(String[] args) throws InterruptedException {
new Thread(() - {
readLock.lock();
try {
System.out.println(子线程);
try {
Thread.sleep(500000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} finally {
readLock.unlock();
}
}).start();Thread.sleep(1000);
writeLock.lock();
try {
System.out.println(主线程);
} finally {
writeLock.unlock();
}
}
二、读写锁的实现原理
ReentrantReadWriteLock还是基于AQS实现的还是对state进行操作拿到锁资源就去干活如果没有拿到依然去AQS队列中排队。 读锁操作基于state的高16位进行操作。 写锁操作基于state的低16为进行操作。 ReentrantReadWriteLock依然是可重入锁。 写锁重入读写锁中的写锁的重入方式基本和ReentrantLock一致没有什么区别依然是state进行1操作即可只要确认持有锁资源的线程是当前写锁线程即可。只不过之前ReentrantLock的重入次数是state的正数取值范围但是读写锁中写锁范围就变小了。 读锁重入因为读锁是共享锁。读锁在获取锁资源操作时是要对state的高16位进行 1操作。因为读锁是共享锁所以同一时间会有多个读线程持有读锁资源。这样一来多个读操作在持有读锁时无法确认每个线程读锁重入的次数。为了去记录读锁重入的次数每个读操作的线程都会有一个ThreadLocal记录锁重入的次数。 写锁的饥饿问题读锁是共享锁当有线程持有读锁资源时再来一个线程想要获取读锁直接对state修改即可。在读锁资源先被占用后来了一个写锁资源此时大量的需要获取读锁的线程来请求锁资源如果可以绕过写锁直接拿资源会造成写锁长时间无法获取到写锁资源。 读锁在拿到锁资源后如果再有读线程需要获取读锁资源需要去AQS队列排队。如果队列的前面需要写锁资源的线程那么后续读线程是无法拿到锁资源的。持有读锁的线程只会让写锁线程之前的读线程拿到锁资源。
三、写锁分析
3.1 写锁加锁流程概述
3.2 写锁加锁源码分析
写锁加锁流程
// 写锁加锁的入口
public void lock() {
sync.acquire(1);
}
// 阿巴阿巴
public final void acquire(int arg) {
if (!tryAcquire(arg)
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();
}
// 读写锁的写锁实现tryAcquire
protected final boolean tryAcquire(int acquires) {
// 拿到当前线程
Thread current Thread.currentThread();
// 拿到state的值
int c getState();
// 得到state低16位的值
int w exclusiveCount(c);
// 判断是否有线程持有着锁资源
if (c ! 0) {
// 当前没有线程持有写锁读写互斥告辞。
// 有线程持有写锁持有写锁的线程不是当前线程不是锁重入告辞。
if (w 0 || current ! getExclusiveOwnerThread())
return false;
// 当前线程持有写锁。 锁重入。
if (w exclusiveCount(acquires) MAX_COUNT)
throw new Error(Maximum lock count exceeded);
// 没有超过锁重入的次数正常 1
setState(c acquires);
return true;
}
// 尝试获取锁资源
if (writerShouldBlock() ||
// CAS拿锁!compareAndSetState(c, c acquires))
return false;
// 拿锁成功设置占有互斥锁的线程
setExclusiveOwnerThread(current);
// 返回true
return true;
}
//
// 这个方法是将state的低16位的值拿到
int w exclusiveCount(c);
state ((1 16) - 1)
00000000 00000000 00000000 00000001 1
00000000 00000001 00000000 00000000 1 16
00000000 00000000 11111111 11111111 (1 16) - 1
运算一个为0必然为0都为1才为1
//
// writerShouldBlock方法查看公平锁和非公平锁的效果
// 非公平锁直接返回false执行CAS尝试获取锁资源
// 公平锁需要查看是否有排队的如果有排队的我是否是head的next
3.3 写锁释放锁流程概述释放锁源码
释放的流程和ReentrantLock一致只是在判断释放是否干净时判断低16位的值
// 写锁释放锁的tryRelease方法
protected final boolean tryRelease(int releases) {
// 判断当前持有写锁的线程是否是当前线程
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
// 获取state - 1
int nextc getState() - releases;
// 判断低16位结果是否为0如果为0free设置为true
boolean free exclusiveCount(nextc) 0;
if (free)
// 将持有锁的线程设置为null
setExclusiveOwnerThread(null);
// 设置给state
setState(nextc);
// 释放干净返回true。 写锁有冲入这里需要返回false不去释放排队的Node
return free;
}