四、Lock的使用
Lock对象比synchronized 的使用更加灵活,并且支持嗅探锁定,多路分支通知
1. ReentrantLock 实现同步
- lock对象的 lock()方法,必须在 finally{ lock.unlock () };释放掉锁
- lock.newCondition()产生的 Condition 对象只在 lock.lock() 与 lock.unlock() 之间有效;
比如 condition.await(); 和 condition.signal();
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class MyService {
private ReentrantLock lock = new ReentrantLock();
public Condition condition = lock.newCondition();
public void serviceA(){
try {
lock.lock();
System.out.println("在这里进行同步业务操作");
}finally {
lock.unlock();
}
}
public void await() {
try {
lock.lock();
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void signal(){
try {
lock.lock();
condition.signal();
} finally {
lock.unlock();
}
}
public void signalAll(){
try {
lock.lock();
condition.signalAll();
} finally {
lock.unlock();
}
}
}
2.使用多个 Condition 实现通知部分线程
同一个 Lock对象可以制造多个 Condition ,让不同的线程使用不同的Condition 就可以达到 只通知使用特定 Condition 的线程的目的。
与 synchronized( obj ) { if( tag ) { obj.wait(); } … obj.notify() } 使用相似,
也可以用 condition 的 await 和 signal / signalAll 实现 n对 n 的生产消费模型
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class MyServiceA {
ReentrantLock lock = new ReentrantLock();
Condition conditionA = lock.newCondition();
Condition conditionB = lock.newCondition();
public void awaitA(){
try {
lock.lock();
conditionA.await();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void signalA(){
try {
lock.lock();
conditionA.signalAll();
} finally {
lock.unlock();
}
}
public void awaitB(){
try {
lock.lock();
conditionB.await();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void signalB(){
try {
lock.lock();
conditionB.signalAll();
} finally {
lock.unlock();
}
}
}
public class MyServiceATest {
public static void main(String[] args) throws InterruptedException {
final MyServiceA myServiceA = new MyServiceA();
Thread tA = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程A开始等待");
myServiceA.awaitA();
System.out.println("线程A被唤醒");
}
});
Thread tB = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程B开始等待");
myServiceA.awaitB();
System.out.println("线程B被唤醒");
}
});
tA.start();
tB.start();
Thread.sleep(1000);
myServiceA.signalA();
}
}
3.Lock锁分为 ‘公平锁’和‘非公平锁’
-
公平锁:根据线程加锁的顺序,即先进先出,来分配锁的获取顺序(趋势是这样,也有少数几个线程可能例外)
-
非公平锁:先加锁的不一定先得锁,可能造成某些线程一直得不到锁,是随机获取的
new ReenTrantLock( true ); // true 公平锁,false 非公平锁
4.ReentrantLock 的常用api
4.1 getHoldCount() 当前锁嵌套层级
当前线程执行 getHoldCount时的得锁次数
private ReentrantLock lock = new ReentrantLock();
public Condition condition = lock.newCondition();
public void serviceA(){
try {
lock.lock();
System.out.println("[1]当前加锁次数:"+lock.getHoldCount());
try {
lock.lock();
System.out.println("[2]当前加锁次数:"+lock.getHoldCount());
}finally {
lock.unlock();
}
}finally {
lock.unlock();
}
}
4.2 getQueueLength() 得到正在竞争等待获取 此 Lock对象的线程 估计个数
10个线程同时竞争一个Lock,1个得锁,9个进入竞争等待。
public void serviceB(){
try {
lock.lock();
System.out.println("线程:"+Thread.currentThread().getName()+"得到锁");
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public class Mytest {
public static void main(String[] args) throws InterruptedException {
final MyServiceB myServiceB = new MyServiceB();
Runnable runnable = new Runnable() {
@Override
public void run() {
myServiceB.serviceB();
}
};
for (int i = 0; i < 10 ; i++) {
Thread thread = new Thread(runnable);
thread.start();
}
Thread.sleep(3000);
int queueLength = myServiceB.lock.getQueueLength();
System.out.println("此时等待得锁的线程数:"+queueLength);
}
}
4.3 getWaitQueueLength( Condition ) 指定 condtion 下wait()状态的线程个数
4.4 hasQueuedThread( Thread ) 指定线程是否在竞争等待得锁
4.5 hasQueuedThreads( )是否有线程在竞争等待得到 此锁
4.6 hasWaiters( Condition ) 指定 condtion 是否有 wait()状态的线程
4.7 isFair() 锁是否公平
4.8 isHeldByCurrentThread() 当前线程是否持有锁
4.9 isLocked() 此锁定是否有任意线程保持
4.10 lock. lockInterruptibly() : 如果当前线程未标记中断,得到锁,已被中断,则异常
4.11 tryLock() 当前锁没有被其它线程保持时,就获得锁;
4.12 tryLock( long , TimeUnit) 指定时间内不断 tryLock()
4.14 condition.awaitUninterruptibly( ) 不可被中断的等待
普通的 await() 方法,在使线程等待期间遇到,线程 interrupt() 的时候,会抛出中断异常
4.15 awaitUntil( long ) 等待一段时间唤醒自己
5. 使用 多个condition 的等待通知模型,可以实现对线程业务执行的排序
下列 线程A 唤醒 线程B 唤醒 线程C 唤醒 线程A 形成闭环 , 线程A是起点 ,
线程A{
if ( tag1 ){
conditionA.await();
}
doSomeThingA();
conditionB.singnalAll();
}
线程B{
if ( tag2 ){
conditionB.await();
}
doSomeThingB();
conditionC.singnalAll();
}
线程C {
if ( tag2 ){
conditionC.await();
}
doSomeThingC();
conditionA.singnalAll();
}
6. 注意事项
- 使用 Condition 的方法或者以 Condition作为参数的方法时,都必须包含 在 lock.lock()后的代码块中
- 使用 await( ) 与 signal( ) 方法的时候,要注意 signal( ) 不要抢先执行了(比如在父线程中时,signal () 代码 可能抢先)
7. 读/写锁 ReenTrantReadWriteLock
为了提高代码的执行效率,Lock 对象的 readLock().lock() 之间是不会互斥的,
而 writeLock().lock() 与读写锁都会互斥;
读锁之间不互斥,写锁与读锁和写锁都会互斥,这样让允许 ‘只读’ 的代码可以异步调用,但是当读发生在同时有写的发生的时候就会互斥。