美文网首页
ReentrantLock简介

ReentrantLock简介

作者: 一路花开_8fab | 来源:发表于2018-07-12 21:50 被阅读0次

在java 1.5中新增加了ReentrantLock类,所谓ReentrantLock的再入特性,是指当一个线程试图获取一个它已经获取的锁时,这个获取动作就自动成功,这是对锁获取粒度的一个概念,也就是锁的持有是以线程为单位而不是基于调用次数.Java 锁实现强调再入性是为了和 pthread 的行为进行区分。
ReentrantLock的语义和synchronized基本相同,与synchronized相比,ReentrantLock使用上更加灵活。

  • 可通过Condition类实现多路通知功能
  • 提供多个便利的方法,如判断是否有线程在排队等待锁
  • 可设置所为公平锁或非公平锁
  • 可以响应中断请求
  • 带超时的获取锁尝试

案例1.使用condition实现线程按顺序执行

public class LockTask {
    private Lock lock = new ReentrantLock();

    private Condition aCondition = lock.newCondition();
    private Condition bCondition = lock.newCondition();
    private Condition cCondition = lock.newCondition();

    private static int controlNum = 0;

    public void printA(){
        lock.lock();
        try{
            while(controlNum != 0){
                aCondition.await();
            }

            System.out.print("A");
            Thread.sleep(1000);
            controlNum = (++controlNum) % 3;

            bCondition.signalAll();
        }catch (Exception e){
            System.out.print(e.getMessage());
        }finally{
            lock.unlock();
        }
    }

    public void printB(){
        lock.lock();
        try{
            while(controlNum != 1){
                bCondition.await();
            }

            System.out.print("B");
            Thread.sleep(1000);
            controlNum = (++controlNum) % 3;

            cCondition.signalAll();
        }catch (Exception e){
            System.out.println(e.getMessage());
        }finally{
            lock.unlock();
        }
    }

    public void printC(){
        lock.lock();
        try{
            while(controlNum != 2){
                cCondition.await();
            }

            System.out.print("C");
            Thread.sleep(1000);
            controlNum = (++controlNum) % 3;

            aCondition.signalAll();
        }catch (Exception e){
            System.out.println(e.getMessage());
        }finally{
            lock.unlock();
        }
    }
}
public class ThreadA extends Thread {
    private LockTask lockTask;

    public ThreadA(LockTask lockTask){
        this.lockTask = lockTask;
    }
    @Override
    public void run() {
        for(int i = 0; i < 10; i++) {
            lockTask.printA();
        }
    }
}
public class ThreadB extends Thread {
    private LockTask lockTask;

    public ThreadB(LockTask lockTask){
        this.lockTask = lockTask;
    }
    @Override
    public void run() {
        for(int i = 0; i < 10; i++) {
            lockTask.printB();
        }
    }
}
public class ThreadC extends Thread {
    private LockTask lockTask;

    public ThreadC(LockTask lockTask){
        this.lockTask = lockTask;
    }
    @Override
    public void run() {
        for(int i = 0; i < 10; i++) {
            lockTask.printC();
        }
    }
}
public class LockMain {
    public static void main(String[] args) {
        LockTask lockTask = new LockTask();

        ThreadA a1 = new ThreadA(lockTask);
        ThreadA a2 = new ThreadA(lockTask);

        ThreadB b1 = new ThreadB(lockTask);
        ThreadB b2 = new ThreadB(lockTask);

        ThreadC c1 = new ThreadC(lockTask);
        ThreadC c2 = new ThreadC(lockTask);

        a1.start();
        a2.start();

        b1.start();
        b2.start();

        c1.start();
        c2.start();
    }
}
线程按顺序调用

案例2 BlockingQueue中的ReentrantLock+Condition。

ArrayBlockingQueue,LinkedBlockingQueue等阻塞队列通过ReentrantLock+Condition实现向队列插入数据和从队列中取数据的语义,下面详细讲解:

    public ArrayBlockingQueue(int capacity, boolean fair) {
        if (capacity <= 0)
            throw new IllegalArgumentException();
        this.items = new Object[capacity];
        lock = new ReentrantLock(fair);
        notEmpty = lock.newCondition();
        notFull =  lock.newCondition();
    }
  • lockInterruptibly()的作用是如果线程未被中断则获取锁定,如果线程已被中断,则抛出异常。
    1.当队列为空时,试图take的线程的正确行为应该是等待入队发生,而不是直接返回,通过notEmpty.await就可以实现。
   public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == 0)
                notEmpty.await();
            return dequeue();
        } finally {
            lock.unlock();
        }
    }

在enquene操作中可以通过notEmpty.signal()唤醒等待take的线程

   /**
     * Inserts element at current put position, advances, and signals.
     * Call only when holding lock.
     */
    private void enqueue(E x) {
        // assert lock.getHoldCount() == 1;
        // assert items[putIndex] == null;
        final Object[] items = this.items;
        items[putIndex] = x;
        if (++putIndex == items.length)
            putIndex = 0;
        count++;
        notEmpty.signal();
    }

2.当队列为满时,试图put的线程的正确行为应该是等待出队发生,而不是直接返回,通过notFull.await就可以实现。

   /**
     * Inserts the specified element at the tail of this queue, waiting
     * for space to become available if the queue is full.
     *
     * @throws InterruptedException {@inheritDoc}
     * @throws NullPointerException {@inheritDoc}
     */
    public void put(E e) throws InterruptedException {
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == items.length)
                notFull.await();
            enqueue(e);
        } finally {
            lock.unlock();
        }
    }

在dequene操作中可以通过notFull.signal()唤醒等待put的线程

    /**
     * Extracts element at current take position, advances, and signals.
     * Call only when holding lock.
     */
    private E dequeue() {
        // assert lock.getHoldCount() == 1;
        // assert items[takeIndex] != null;
        final Object[] items = this.items;
        @SuppressWarnings("unchecked")
        E x = (E) items[takeIndex];
        items[takeIndex] = null;
        if (++takeIndex == items.length)
            takeIndex = 0;
        count--;
        if (itrs != null)
            itrs.elementDequeued();
        notFull.signal();
        return x;
    }

相关文章

网友评论

      本文标题:ReentrantLock简介

      本文链接:https://www.haomeiwen.com/subject/odfdpftx.html