android 多线程 — reentrantLock 重入锁

reentrantLock 、 condition 是 JAVA 1.6 时推出的,也是用来实现多线程同步的,和 synchronized 干的事一样,用法页差不多,但是比 synchronized 要灵活

其中 reentrantLock 叫重入锁,condition 是阻塞条件,我们直接啊看用法来了解这2个类

实现锁的功能

public class MyService {

    private Lock lock = new ReentrantLock();

    public void testMethod() {
        lock.lock();
        for (int i = 0; i < 5; i++) {
            System.out.println("ThreadName=" + Thread.currentThread().getName()
                    + (" " + (i + 1)));
        }
        lock.unlock();
    }
}

synchronized 因为本身是关键字,只能实现标记,本身不含锁,需要我们指定对象锁。而新的 API ReentrantLock 本身就是把锁,不用再依托别人了。

ReentrantLock 在使用上类似于 synchronized 的同步代码块,我们直接在需要的位置, new 一个 ReentrantLock 锁出来,然后 lock() 就能锁定同步,unlock() 就能解锁同步,注意unlock() 方法推荐写在 finnaly 里面

Condition 来阻塞线程并释放锁

public class MyService {

    private Lock lock = new ReentrantLock();
    private Condition condition=lock.newCondition();
    public void testMethod() {
        
        try {
            lock.lock();
            System.out.println("开始wait");
            condition.await();
            for (int i = 0; i < 5; i++) {
                System.out.println("ThreadName=" + Thread.currentThread().getName()
                        + (" " + (i + 1)));
            }
        } catch (InterruptedException e) {
            // TODO 自动生成的 catch 块
            e.printStackTrace();
        }
        finally
        {
            lock.unlock();
        }
    }
}

Condition 的 await() 会阻塞当前线程,并释放锁、signal() 方法唤醒 wait 阻塞的线程。

Condition.awiat() = Object.wait()
Condition.signal() = Object.notify()
Condition.signalAll() = Object.notifyAll()

reentrantLock 和 condition 都是成对出现,一个 reentrantLock 里面可以有多个 condition 阻塞条件对象,需要知道的是 conditionA await 阻塞的线程对象会放在这个 conditionA 的阻塞队列里,也只能由 conditionA signalAll 唤醒。换一个 condition 对象 conditionB 是不管用的

线程池的阻塞队列里面大量使用了 reentrantLock ,condition ,一般我们直接用 reentrantLock 、 condition 的时候比较少,虽然 reentrantLock 的性能比 synchronized 要好,但是在使用上没有 synchronized 方便啊,synchronized 关键字乙方就完了,我们就不用管了。不过 reentrantLock 、 condition 的 API 我们要熟,很多时候看别人的代码时会用到。

ReentrantLock类的方法


  • getHoldCount()
    查询当前线程保持此锁的次数,也就是执行此线程执行lock方法的次数

  • getQueueLength()
    返回正等待获取此锁的线程估计数,比如启动10个线程,1个线程获得锁,此时返回的是9

  • getWaitQueueLength(Condition condition)
    返回等待与此锁相关的给定条件的线程估计数。比如10个线程,用同一个condition对象,并且此时这10个线程都执行了condition对象的await方法,那么此时执行此方法返回10

  • hasWaiters(Condition condition)
    查询是否有线程等待与此锁有关的给定条件(condition),对于指定contidion对象,有多少线程执行了condition.await方法

  • hasQueuedThread(Thread thread)
    查询给定线程是否等待获取此锁

  • hasQueuedThreads()
    是否有线程等待此锁

  • isFair()该锁是否公平锁

  • isHeldByCurrentThread() 当前线程是否保持锁锁定,线程的执行lock方法的前后分别是false和true

  • isLock()
    此锁是否有任意线程占用

  • lockInterruptibly()
    如果当前线程未被中断,获取锁

  • tryLock()
    尝试获得锁,仅在调用时锁未被线程占用,获得锁

  • tryLock(long timeout TimeUnit unit)
    如果锁在给定等待时间内没有被另一个线程保持,则获取该锁

Lock类分公平锁和不公平锁,公平锁是按照加锁顺序来的,非公平锁是不按顺序的,也就是说先执行lock方法的锁不一定先获得锁

reentrantLock 的确要灵活的多,我们不用使用别的锁了,在想要的位置直接 new 一把锁,阻塞的条件可以有多个,可以实现复杂的操作,这是阻塞队列的核心。另外 reentrantLock 可以使用 tryLock 尝试获得锁,可以避免一直卡在这里竞争锁。reentrantLock 对于高玩来说是个可以极大发挥的东西,但是对于 coder 们来说,还是在项目纵谨慎使用。

下面是一个对 ReentrantLock 中肯的评价

既然如此,我们什么时候才应该使用 ReentrantLock 呢?答案非常简单 —— 在确实需要一些 synchronized 所没有的特性的时候,比如时间锁等候、可中断锁等候、无块结构锁、多个条件变量或者锁投票。 ReentrantLock 还具有可伸缩性的好处,应当在高度争用的情况下使用它,但是请记住,大多数 synchronized 块几乎从来没有出现过争用,所以可以把高度争用放在一边。我建议用 synchronized 开发,直到确实证明 synchronized 不合适,而不要仅仅是假设如果使用 ReentrantLock “性能会更好”。请记住,这些是供高级用户使用的高级工具。(而且,真正的高级用户喜欢选择能够找到的最简单工具,直到他们认为简单的工具不适用为止。)。一如既往,首先要把事情做好,然后再考虑是不是有必要做得更快。

参考资料:


  • java多线程系列(四)---ReentrantLock的使用
  • Java中的ReentrantLock和synchronized两种锁定机制的对比
  • Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition
  • 关于synchronized和ReentrantLock之多线程同步详解

你可能感兴趣的:(android 多线程 — reentrantLock 重入锁)