并发编程-ReentrantLock类走读
程序员文章站
2022-05-04 20:54:54
...
背景
并发编程中需要使用锁在进行线程间同步和禁止同时访问共有资源的。下面走读下juc包中的ReentrantLock
血缘关系
ReentrantLock血缘
- Lock接口类,属于jdk中juc包,锁的基类(不包括synchronize关键字)
- ReentrantLock,可重入锁,实现了Lock
Lock的其他派生类血缘
可以看到,在juc的包中,除了读写锁,就没其他的锁实现Lock了,可见原生的锁种类并不繁多(自旋锁、乐观悲观锁等都是一种概念和方法,在java中并没有特殊的实现类)。
源码走读
Lock接口类
- lock,获取锁资源(不可中断)
- lockInterruptibly,获取锁资源,但是可以在等待中被中断
- tryLock,尝试获取锁资源,如果获取不到,则返回false
- unlock,释放锁
- newCondition,创建同步实例
ReentrantLock类
- getHoldCount,查看同时锁住的线程个数
- isHeldByCurrentThread,是否被当前线程获取锁
- isLocked,是否被获取
- isFair,是否是公平锁(构造函数中可以指定)
- hasQueuedThreads,是否有线程在排队获取锁
- getQueueLength,获取排队线程数量
- hasWaiters,指定condition是否有await(需要在lock内使用)
- getWaitQueueLength,获取await的数量(需要在lock内使用)
使用ReentrantLock需要注意一下几点(之后的实战中也将体现):
-
线程在lock后的操作时,其他线程不能再lock
,这是锁的最基础使用了 - 使用await和signal需要在lock之后,unlock之前
-
线程在lock之后,又进入await,那么其他线程可以继续lock
,之后的实战将有所体现
实战
isxxx方法实战
public class LockDemo {
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
// false,是否是公平锁
System.out.println(lock.isFair());
lock.lock();
// true,是否已经上锁
System.out.println(lock.isLocked());
// true,是否被当前线程上锁
System.out.println(lock.isHeldByCurrentThread());
lock.unlock();
}
}
holdxxx方法实战
public class LockDemo {
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
// false
System.out.println(lock.isHeldByCurrentThread());
lock.lock();
// 1,重入次数
System.out.println(lock.getHoldCount());
// true
System.out.println(lock.isHeldByCurrentThread());
lock.unlock();
lock.lock();
lock.lock();
// 2,重入次数
System.out.println(lock.getHoldCount());
lock.unlock();
lock.unlock();
}
}
queuedxxx方法实战
public class LockDemo {
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
createThread();
createThread();
createThread();
// 为了能让线程启动完毕,进行10ms的睡眠
Thread.sleep(10);
System.out.println(lock.hasQueuedThreads());
System.out.println(lock.getQueueLength());
}
public static void createThread() {
new Thread(() -> {
try {
lock.lock();
System.out.println("上锁");
while (true);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
System.out.println("解锁");
}
}).start();
}
}
结果分析:
- 创建了三个线程,第一个线程一直在跑while循环,另外两个线程则阻塞在lock.lock()的位置(
因为第一个线程一直占用cpu,所以另外两个线程需要等待锁的释放
)。 - hasQueuedThreads,返回结果true,因为后面两个线程在等待
- getQueueLength,返回结果2,因为一共2个线程在等待。
awaitxxx方法实战
public class LockDemo {
private static ReentrantLock lock = new ReentrantLock();
private static Condition condition = lock.newCondition();
public static void main(String[] args) throws InterruptedException {
createThread();
createThread();
createThread();
Thread.sleep(10);
System.out.println(lock.hasQueuedThreads());
System.out.println(lock.getQueueLength());
lock.lock();
System.out.println(lock.hasWaiters(condition));
System.out.println(lock.getWaitQueueLength(condition));
condition.signalAll();
lock.unlock();
}
public static void createThread() {
new Thread(() -> {
try {
lock.lock();
System.out.println("上锁" + Thread.currentThread().getName());
condition.await();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
System.out.println("解锁" + Thread.currentThread().getName());
}
}).start();
}
}
结果分析:
- 开始创建三个线程,但是都阻塞在await上(
注意:lock之后进行await,不再占用cpu实现片,其他线程可以再调用lock
) - 没有线程在排队,而是都进入了await的状态
- hasWaiters,返回结果true
- getWaitQueueLength,返回结果3,说明先创建的三个线程都在await
- 调用condition.signalAll(),对三个线程进行**
点评
- lock可以new多个Condition,进行灵活的控制,相比Object的wait+notify要灵活、安全。
- 据说在synchronized优化后,同ReentrantLock的效率一样,特意写代码对比了一下,发现ReentrantLock的效率高一些,但是优势不明显
public class LockDemo {
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start("synchronized lock");
for (int i = 0; i < 20000000; i++) {
testSynchronizedLock();
}
stopWatch.stop();
stopWatch.start("reentrant lock");
for (int i = 0; i < 20000000; i++) {
testReentrantLock();
}
stopWatch.stop();
System.out.println(stopWatch.prettyPrint());
}
public static synchronized void testSynchronizedLock() {
}
public static void testReentrantLock() {
lock.lock();
lock.unlock();
}
}
推荐阅读
-
并发编程(五)——AbstractQueuedSynchronizer 之 ReentrantLock源码分析
-
Java并发编程之常用的辅助类详解
-
[Java 并发编程实战] 设计线程安全的类的三个方式(含代码)
-
java并发编程——通过ReentrantLock,Condition实现银行存取款
-
并发编程(一)------同步类容器
-
多线程编程学习九(并发工具类).
-
Java并发编程之ReentrantLock学习
-
Java并发编程之可重入锁ReentrantLock
-
JAVA并发编程(三):同步的辅助类之闭锁(CountDownLatch)与循环屏障(CyclicBarrier)
-
Java多线程并发编程中并发容器第二篇之List的并发类讲解