ReentrantLock 是在编程对象层次的可重入锁,synchronized 是关键字,是语言特性上的锁。
ReentrantLock 相对于 synchronized 而言更灵活,ReentrantLock 能够通过 Condition 指定线程唤醒,是否响应中断,尝试获取锁,获取当前锁状态(等待线程数等),是否是公平锁。
| 关键方法 | 说明 |
|---|---|
| boolean trylock() | 尝试获取锁,不阻塞,成功返回 true,失败返回 false。 |
| boolean trylock(long timeout,TimeUnit unit) | 等待指定时间后再尝试获取锁,成功返回true,失败返回false。 |
| void lock() | 获取锁,该方法会阻塞,直至获取到锁。 |
| void lockInterruptibly() | throws InterruptedException 获取锁,该方法会阻塞,直至获取到锁。若调用该方法是,当前线程已经 interrupt 则抛出异常。 |
| Boolean isLocked() | 当前锁是否已经有线程占用,是则返回true,否则返回false。 |
| void unlock() | 释放锁,该方法建议放在finally中使用。 |
| int getHoldCount() | ReentrantLock支持重入,此方法返回同一个线程当中,lock的总次数-unlock总次数 |
| Condition newCondition() | 返回一个Condition变量,用于指定线程的等待-唤醒 |
| 关键方法 | 说明 |
|---|---|
| void awaitUninterruptibly() | 使当前线程等待,调用这个方法时,即使当前线程interrupt,也不会报异常。 |
| void await() | 使当前线程等待,调用这个方法时,如果当前线程interrupt则报异常。 |
| void signal() | 唤醒等待的其中一个线程。 |
| void signalAll() | 唤醒所有等待线程。 |
| long awaitNanos(long nanosTimeout) | throws |
| InterruptedException | 等待指定时间,调用这个方法时,若当前线程interrupt,则报异常。 |
static ReentrantLock lock = new ReentrantLock();
static Condition condition = lock.newCondition();
static Runnable runnable = new Runnable() {
public void run() {
Thread.currentThread().interrupt();
System.out.println(Thread.currentThread().getName()+ "start");
try {
lock.lock();
condition.awaitUninterruptibly();} catch (Exception e) {}
finally {
lock.unlock();
}
System.out.println(Thread.currentThread().getName()+ "end");
}
};
public static void main(String[] arg) throws Exception
{
new Thread(runnable).start();
new Thread(runnable).start();
Thread.sleep(5000);
{
lock.lock();
condition.signalAll();
System.out.println("unlock");
lock.unlock();
}
ReentrantReadWriteLock有两把锁,分别是读锁和写锁,其中 读操作与读操作允许并发,读写 写读 写写 必须是互斥执行。
| 关键方法 | 说明 |
|---|---|
| ReentrantReadWriteLock.WriteLock writeLock() | 获取写锁 |
| ReentrantReadWriteLock.ReadLock readLock() | 获取读锁 |
ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
// Condition readCon = lock.readLock().newCondition(); // 读锁不能创建condition
Condition writeCon = lock.writeLock().newCondition();
当有大量读线程少量写线程的时候,写线程会迟迟获取不到锁而处于等待状态。
StampedLock 是 ReentrantReadWriteLock 的改进,stampedLock 提供读锁、写锁、乐观读锁。读锁和写锁和 ReentrantLock 功能一样,读读并发,读写、写读、写写互斥。获取锁后会返回一个long值,释放锁需要将这个值传入。
| 关键方法 | 说明 |
|---|---|
| long readLock() | 获取写锁 |
| long writeLock() | 获取读锁 |
| long tryOptimisticRead() | 获取乐观读锁 |
| void unlock(long stamp) | 释放读锁或写锁,全能型 |
| void unlockWrite(long stamp) | 只能释放写锁,若释放读锁则抛异常 |
| void unlockRead(long stamp) | 只能释放读锁,若释放写多则抛异常 |
基本使用方法:
StampedLock lock = new StampedLock();
long locklong = lock.readLock();
……
lock.unlockRead(locklong);
long locklong = lock.writeLock();
……
lock.unlockWrite(locklong);
乐观读锁是先获取乐观读锁,再读数据,读完后还需要再检查刚刚读数据期间是否有写锁,若有则必须等待写锁完成后再次尝试,否则则获取读锁。
LockSupport
LockSupport 是用来创建锁和其他同步类的基本阻塞原语, 它的park() 和 unpark() 的作用分别是阻塞线程和解除阻塞线程
| 关键方法 | 说明 |
|---|---|
| static void park() | 将当前锁阻塞 |
| static void unpark(Thread thread) | 对指定线程解除阻塞 |
基本使用方法:
LockSupport.park();
LockSupport.unpark(Thread)
与 Object 的 wait 方法不同,Object 的 wait 执行前需要获得 synchronized 锁,LockSupport 不需要。
定义一个共享int类型变量,写线程在获取锁后对其进行累加操作,加到10^7 。 读线程获取锁后,读取变量。 下面测试不同的锁效率,表格时间单位为秒。
对int类型变量进行累加到 10^7
| 锁类型 | 5读5写 | 10读10写 | 16读4写 | 19读1写 |
|---|---|---|---|---|
| synchronized | 0.9 | 0.7 | 1.6 | 11.3 |
| ReentrantLock | 0.6 | 0.6 | 2.2 | 5.6 |
| ReentrantReadWriteLock | 1.9 | 1.9 | 25.4 | 190.7 |
| StampedLock | 0.3 | 0.5 | 0.7 | 3.3 |
| StampedLock | 乐观读锁 | 1.8 | 2.1 | 2.3 |

