Lock的使用
前言
这是java多线程基础理论知识的第6篇文章,预计这一系列的分享还有2篇。今天要分享的内容是进阶版的线程锁,之前的线程锁主要是synchronized关键字的方法或者语句块,这种用法比较生涩和不友好;本章推荐使用Lock对象来替换synchronized,因为Lock类有很多独特的优势,而且还有助于学习并发包中的源码。
主要内容如下:
ReentrantLock类
ReentrantLock
声明方式如下:
private Lock lock = new ReentrantLock();
使用方式如下:
//程序片段
try{
//获得锁
lock.lock();
...
}catch(InterruptedException e){
e.printStackTrace();
}finally {
//释放锁
lock.unlock();
}
调用lock.lock()代码的线程就持有了“对象监视器”,其他线程只有等待锁被释放时再次争抢。和synchronized关键字一样,线程简间的执行顺序是随机的。
Conditon的使用
关键字synchronized与wait()和notify()/notifyAll()方法相结合可以实现等待/通知模式。类ReentrantLock也具有相同的功能,但需要借助Condition对象,该对象支持多路通知功能,就是在一个Lock对象里可以创建多个Condition(对象监视器)实例,线程对象可以注册在指定的Condition中,从而可以有选择地进行线程通知,在线程调度上更加灵活。
声明方式如下:
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
使用方式如下:
try{
lock.lock();
...
//等待
condition.await();
...
}catch(InterruptedException e){
e.printStackTrace();
}finally {
lock.unlock();
}
注意,在condition.await()和condition.signal()方法调用前必须调用lock.lock()方法获得同步监视器。
总结:
- Object类中的wait()(代码行处释放锁)方法相当于Condition类中的await()方法。
- Object类中的wait(long timeout)方法相当于Condition类中的await(long time,TimeUnit unit)方法。
- Object类中的notify()(执行完释放锁)方法相当于Condition类中的signal()方法。
- Object类中的notifyAll()方法相当于Condition类中的signalAll()方法。
公平锁与非公平锁
注
- FIFO:先来先得,先进先出。
- 抢占机制:随机获得锁。
使用方式(true为公平锁,false为非公平锁):
lock = new ReentrantLock(boolean)
公平锁的执行结果是呈基本有序的,不是完全有序;非公平锁的执行结果是基本乱序的。
方法大总结
这里用表格列举一下,lock经常使用的方法。
方法名 | 参数 | 描述 |
---|---|---|
getHoldCount | / | 查询当前线程保持此锁定的个数,也就是调用lock()方法的个数。 |
getQueueLength | / | 返回正等待获取此锁定的线程估计数。 |
getWaitQueueLength | Condition | 等待与此锁定相关的给定条件Condition的线程估计数 。 |
hasQueuedThread | Thread | 查询指定的线程是否等待获取此锁。 |
hasQueuedThreads | / | 查询是否有线程等待获取此锁。 |
hasWaiters | Condition | 查询是否有线程正在等待与此锁有关的Condition条件。 |
isFair | / | 判断是不是公平锁 ,ReentrantLock默认使用非公平锁。 |
isHeldByCurrentThread | / | 查询当前线程是否保持此锁定。 |
isLocked | / | 查询此锁定是否由任意线程保持。 |
lockInterruptibly | / | 如果当前线程未被中断,则获取锁定;如果中断则抛出异常。 |
tryLock | / | 仅仅在调用时锁定未被另一个线程保持的锁。 |
tryLock | long,TimeUnit | 如果锁在给定的时间内没有被另一个线程保持,且当前线程未被中断,则获取该锁定。 |
awaitUninterruptibly | / | 忽略中断的await方法,不抛出异常。 |
awaitUntil | 时间戳 | 线程在等待时间到达前,可以被其他线程提前唤醒的await方法。 |
ReentrantReadWriteLock类
ReentrantLock类具有完全互斥的效果,即同一时间只能有同一个线程在执行ReentrantLock.lock()方法后面的任务。这样做虽然很安全,但是效率很低下,这里介绍一种读写锁ReentrantReadWriteLock类,读写锁表示有两个锁,一个是读相关操作的锁,也称共享锁;一个是写相关操作的锁,也称排他锁。
共享锁声明方式如下:
lock.readLock().lock()
lock.readLock().unlock()
排他锁声明方式如下:
lock.writeLock().lock()
lock.writeLock().unlock()
读读共享
共享锁允许多个线程同时执行lock()方法后面的代码。
写写互斥
互斥锁同一时间只允许一个线程执行lock()方法后面的代码。
读写互斥
读写操作是互斥的,读完之后才能写。
写读互斥
写读操作是互斥的,写完之后才能读。只要出现写操作锁,就都是互斥的。