Java线程之Lock实现锁的管理

来源:互联网 发布:淘宝网店出售平台 编辑:程序博客网 时间:2024/06/05 04:37

1. 两个通用类

ReentrantLock类和ReentrantReadWriteLock类。

2. 作用与区别

  • 都可以实现类似synchronized关键字的功能,通过lock()方法和unlock()方法实现锁的获取与释放。
  • 不同点在于ReentrantLock具有完全互斥排他的效果,即同一时间只有一个线程在执行ReentrantLock.lock()方法后面的任务,不管执行的内容是否都需要同步执行。而ReentrantReadWriteLock有两个锁,一个是读操作相关的锁,也称为共享锁;另一个是写操作相关的锁,也叫排他锁。
  • 读写锁的规则互斥规则为读锁之间不互斥,读锁与写锁互斥(先读后写或先写后读均互斥),写锁与写锁互斥。

3. 公平锁与非公平锁

  锁Lock分为“公平锁”和“非公平锁”,公平锁表示线程获取锁的顺序是按照线程加锁的顺序来分配的,即先来先得的FIFO先进先出顺序。而非公平锁就是一种获取锁的抢占机制,是随机获得锁的,和公平锁不一样的就是先来的不一定先得到锁,这个方式可能造成某些线程一直拿不到锁。
  公平锁/非公平锁可以通过构造器ReentrantLock(boolean isFair)来创建,默认情况下,ReentrantLock类使用的是非公平锁。

4. Lock常用方法

  • getHoldCount()
    查询当前线程保持此锁定的个数,也就是调用lock方法的次数。
  • getQueueLength()
    返回正等待获取此锁定的线程估计数。
  • getWaitQueueLength(Condition condition)
    返回等待与此锁定相关的给定条件Condition的线程估计数。
  • hasQueuedThread(Thread thread)
    查询指定的线程是否正在等待获取此锁定。
  • hasQueuedThreads()
    查询是否有线程正在等待获取此锁定。
  • hasWaiters(Condition condition)
    查询是否有线程正在等待与此锁定有关的condition条件。
  • isFair()
    判断是不是公平锁
  • isHeldByCurrentThread()
    查询当前线程是否保持此锁定。
  • isLocked()
    查询此锁定是否由任意线程保持。
  • lockInterruptibly()
    如果当前线程未被中断,则获取锁定,如果已经被中断则出现异常。(如果用lock(),即使线程被中断,也不会出现异常,正常执行)。
  • tryLock()
    尝试获取锁,若获取成功,标记下是该线程获取到了锁,然后返回true;若获取失败,此时直接返回false。
  • tryLock(long timeout, TimeUnit unit)
    如果锁定在给定等待时间内没有被另一个线程保持,且当前线程未被中断,则获取该锁定。
  • lock()、lockInterruptibly()、tryLock()之间的关系
    • 使用lock()获取锁,若获取成功,标记下是该线程获取到了锁(用于锁重入),然后返回。若获取失败,这时跑一个for循环,循环中先将线程阻塞放入等待队列,当被调用signal()时线程被唤醒,这时进行锁竞争(因为默认使用的是非公平锁),如果此时获取到了锁那么就返回,如果没获取到那么再次放入等待队列,等待唤醒,如此循环。其间就算外部调用了interrupt(),循环也会继续走下去。一直到当前线程获取到了这个锁,此时才处理interrupt标志,若有,则执行 Thread.currentThread().interrupt(),结果如何取决于外层的处理。
    • 和lock()相比,lockInterruptibly()只有略微的修改,for循环过程中,如果检测到interrupt标志为true,则立刻抛出InterruptedException异常,这时程序便通过异常直接返回到最外层了,由外层继续处理,因此使用lockInterruptibly()时必须捕捉异常。
    • 使用tryLock()尝试获取锁,若获取成功,标记下是该线程获取到了锁,然后返回true;若获取失败,此时直接返回false,告诉外层没有获取到锁,之后的操作取决于外层。

5. ReentrantLock代码示例

synchronized方法实现

public class MyService {    public void testMethod() {        synchronized (this) {            // 功能代码        }    }}

Lock方法实现

public class MyService {    private Lock lock = new ReentrantLock();    public void testMethod() {        lock.lock();        // 功能代码        lock.unlock();    }}

以上两种实现方式的执行效果是一样的。

6. ReentrantReadWriteLock代码示例

读锁

public class Service {    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();    public void read() {        try {            lock.readLock().lock();            // 业务代码        } finally {            lock.readLock.unlock();        }    }}

写锁

public class Service {    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();    public void write() {        try {            lock.writeLock().lock();            // 业务代码        } finally {            lock.writeLock.unlock();        }    }}

7. 类Condition

  • 作用:通过await()方法和signal()方法可以实现等待/通知的功能,类似于Object的wait()和notify()方法,不过使用Condition更灵活,如“选择性通知”。
    • Object类中的wait()方法相当于Condition类中的await()方法。
    • Object类中的wait(long timeout)方法相当于Condition类中的await(long time, TimeUnit unit)方法。
    • Object类中的notify()方法相当于Condition类中的signal()方法。
    • Object类中的notifyAll()方法相当于Condition类中的signalAll()方法。
  • 创建方式:
Lock lock = new ReentrantLock();Condition condition = lock.newCondition();
  • 注意事项:在condition.await()和condition.signal()方法调用之前需要调用lock.lock()代码获得同步监视器。

8. Condition常用方法

  • awaitUninterruptibly()
    类似await(),不过在等待过程中被中断时不抛中断异常。
  • awaitUntil(Date deadLine)
    类似await(),不过在等待时间到达后,如果可获取到锁,则自动唤醒。在等待时间到达前,也可以被其他线程唤醒。
原创粉丝点击