Chapter04 Lock的使用
1.ReentrantLock
synchronized可以实现线程之间的同步互斥,在jdk1.5中新增的ReentrantLock类能实现同样的效果,并且功能更加强大,新增了嗅探锁定、多路分支通知等。
1.ReentrantLock实现同步
|
|
|
|
|
|
不同方法使用同一ReentrantLock,类似不同方法上加synchronized。
|
|
|
|
|
|
执行结果每次输出一条记录,同步执行。
2. 使用Condition实现等待/通知
synchronized与wait和notify/notifyAll 可以实现等待通知模式。
ReentrantLock也可以实现同样的功能,借助于Condition对象可以实现多路通知功能,在线程调度上更加灵活。
1. 简单示例
|
|
|
|
|
|
执行出现异常
|
|
执行出现了异常,此异常与线程未在synchronized代码块中调用wait()方法一致。
如何解决?
将ConditionWaitNotifyError修改如下,在调用await方法前,先获取锁
2. 正确使用Condition实现等待/通知
使用Condition的await和signal可以实现synchronized对应的wait和notify方法。来实现等待/通知
|
|
|
|
|
|
执行结果
|
|
由日志可清晰地看到执行过程
3. 使用多个Condition实现部分通知
|
|
|
|
|
|
|
|
执行结果
|
|
由此可以控制线程的指定唤醒,功能比notify更加强大
4. 使用Condition实现两个线程交替调用
使用condition的await和signal可以实现线程的互相调用。
|
|
|
|
|
|
|
|
执行结果
5. ReentrantLock的一些方法
1)int getHoldCount()的作用是查询当前线程保持此锁定的个数,也就是调用lock的次数。
|
|
|
|
执行结果
|
|
2)int getQueueLength 返回正等待获取此锁定线程估计数
Thread0-10 sleep
Thread0 lock -> 1-10 wait
getQueueLength() ->10
3)int getWaitQueueLength(Condition condition) 返回等待与此锁定相关的给定条件Condition的线程估计数
4)boolean hasQueueThread(Thread thread)/boolean hasQueueThreads() 查询是否有线程正在等待获取此锁
|
|
|
|
执行结果如下,A获取到锁,进入等待方法
5)boolean hasWaiters(Condition condition) 查询是否有线程正在等待与此锁有关的condition条件
|
|
|
|
执行结果
|
|
6)isFair() 判断是不是公平锁
ReentrantLock构造如下,默认创建的是NonfairSync,非公平锁。
|
|
7)boolean isHeldByCurrentThread() 查询当前线程是保持锁定
|
|
结果如下
8)boolean isLocked() 查询是否已经被上锁
|
|
|
|
9)void lockInterruptibly() 如果当前线程未被中断,则获取锁定,如果已经被中断,则未出现异常
先看一个普通lock的例子
|
|
|
|
执行结果如下
如果将LockInterruptiblyService修改为如下
再次执行,结果如下
|
|
线程B已经被中断,如果线程B尚未获取锁,此时B线程已经别声明为中断,此时将抛出异常,如果B已经开始执行,再声明为异常,此时B线程已经开始执行,声明为异常已经无效。只有使用之前介绍的在B中捕获中断标记,否则B将一直执行下去。
10)boolean tryLock/tryLock(long timeout,TimeUnit unit) 仅在lock未被另一个线程保持的情况下,才获得该锁定。
|
|
11)awaitUninterruptibly() awaitUnti() await状态的线程,中断后不会出现异常。
12)使用Condition实现顺序执行
|
|
|
|
|
|
|
|
|
|
执行结果如下:
|
|
2.ReentrantReadWriteLock
ReentrantLock具有完全互斥排他的效果,及同一时间只有一个线程在执行ReentrantLock.lock()方法后面的任务,虽然保证了线程安全,但效率低下。
JDK提供了另外一种锁,ReentrantReadWriteLock,使用它可以加快运行效率,在某些不需要操作实例变量的方法中,完全可以使用读写锁来提升该方法的代码运行速度。
读写锁表示有两个锁,一个读操作相关的锁,称为共享锁;另外一种是写相关的锁,也叫排他锁。也就是读个读锁之间不胡扯,读锁与写锁互斥,写锁与写锁互斥。在没有线程Thread进行写操作时,进行读取操作的多个Thread都可以获得读锁客人进入写入操作的Thread只有获取写锁后才能进行写入操作。即同一时间可以多个Thread读,只允许一个Thread进行写操作
1.读读共享
|
|
执行结果马上展示两条记录
2.写写互斥
|
|
执行为依次进入锁