Java8 API学习25 - java.util.concur
和使用关键字synchronized
相比, 简单来说, 锁可以做到更加具体的操作, 因此能够在一定程度上简化编码. 参考java文档中对Lock
接口的描述:
Lock implementations provide more extensive locking operations than can be obtained using
synchronized
methods and statements
Lock
public interface Lock
这个接口在java中一共有3个实现, ReentrantLock以及ReentrantReadWriteLock.ReadLock和ReentrantReadWriteLock.WriteLock, 后面两个是ReentrantReadWriteLock的静态类.
先不考虑上述实现类, 我们先来看一下这个接口中声明的方法. 显然这些方法是一个锁的实现类必不可少的内容.
void lock() //阻塞加锁, 直到获得锁
//同上, 但是阻塞过程可以被thread.interrupt()打断, 然后抛出异常, 相当于放弃了加锁请求
void lockInterruptibly() throws InterruptedException
void unlock()
boolean tryLock() //如果锁没有被占用, 就加锁并返回true, 否则返回false
boolean tryLock(long, TimeUnit) //在一定时间内tryLock
Condition newCondition() //见第3部分
ReentrantLock
public class ReentrantLock implements Lock, java.io.Serializable
ReentrantLock的构造方法
ReentrantLock()
ReentrantLock(boolean)
ReentrantLock()
等价于ReentrantLock(false)
, 会使用非公平锁; ReentrantLock(true)
会使用公平锁. 公平锁的机制简单来说就是一个线程队列, 会把锁分配给请求等待时间最长的线程, 但是这个机制不能保证其调度的公平性(关于锁的公平性请参考其他人的文章). 显然公平锁的效率会低于非公平锁.
公平锁和非公平锁是ReentrantLock中的两个非public的静态类, 分别叫FairSync和NonfairSync.
ReentrantLock的对象方法
ReentrantLock是可重入锁的意思, 简单来说就是同一个线程再次进入同步代码的时候, 可以使用自己已经获取到的锁. 实际上synchronized
也是可重入的. 在实现Lock接口声明方法之外, ReentrantLock还定义了一些方法, 大部分方法是和可重入特性相关的.
//返回当前线程持有锁的次数
int getHoldCount()
//返回所有正在请求锁的线程数; 显然这个数未必准确因为随时可能有线程提出请求或放弃请求
final int getQueueLength()
//返回根据Condition请求锁的线程数, *我觉得这个方法名起的很烂
int getWaitQueueLength(Condition)
//查询某个线程是否请求了这个锁
boolean hasQueuedThread(Thread)
//查询是否存在线程请求这个锁
final boolean hasQueuedThreads()
//查询是否有线程根据Condition等待这个锁
boolean hasWaiters(Condition condition)
//是否是公平锁
final boolean isFair()
//是否被当前线程占用
boolean isHeldByCurrentThread()
//是否被任意线程占用
boolean isLocked()
总结
由于线程对锁的获取是不确定的, 因此包括getHoldCount
在内, 上述很多方法并不能保证结果的实时性.
Condition
在Lock接口中, newCondition
方法返回了一个Condition, 下面说明一下这个接口的作用
public interface Condition
在复杂的多线程环境下, 使用Condition可以更加清晰地解决问题, java文档中的说法是"to give the effect of having multiple wait-sets per object, by combining them with the use of arbitrary Lock
implementations". 为了便于区分, Condition中等待-唤醒的方法分别叫await
和signal
.(但是由于Condition的实现类也继承于Object, 所以仍然可以使用wait
和notify
方法, 虽然没什么用)
Lock中每一次调用newCondition
都会生成一个新的Condition对象, 显然不同的Condition直接是没有什么关系的.
Condition中声明的方法
//如果被中断, 抛出异常
void await() throws InterruptedException;
/*一定时间内await, 但和void wait(timeout)不同, 如果被唤醒, 返回true,
如果时间到, 返回false*/
boolean await(long time, TimeUnit unit) throws InterruptedException;
//同上
boolean awaitUntil(Date deadline) throws InterruptedException;
//时间单位为纳秒; 返回值为唤醒时剩余时间
long awaitNanos(long nanosTimeout) throws InterruptedException;
//无视中断, 谨慎使用
void awaitUninterruptibly()
//唤醒某一个await状态, 通常是随机选择
void signal()
//唤醒所有await状态
void signalAll()
IllegalMonitorStateException
Condition的await
和signal
操作必须在当前线程获得锁的情况下才能使用, 否则会抛出该异常, 表示当前状态下不能对Condition进行操作.
类似地, Object.wait
和Object.notify
也要在synchronized
方法或代码块中使用, 否则也会抛出这个异常.
Condition的实现类
调用ReentrantLock.lock.newConditon().getClass()
方法返回结果为java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject
, 其中AbstractQueuedSynchronizer
是一个public的抽象方法, 但是在java包中其实现类都是非public的, 这个抽象类可能是用来方便其他人实现一个同步锁或其中的队列. 因此没有必要说明这个类.