ReentrantLock 详解

3 篇文章 0 订阅
2 篇文章 0 订阅

ReentrantLock 介绍

  • ReentrantLock是一个可重入的互斥锁,又被称为“独占锁”。
  • ReentrantLock锁在同一个时间点只能被一个线程锁持有;可重入表示,ReentrantLock锁可以被同一个线程多次获取。
  • ReentraantLock是通过一个FIFO的等待队列来管理获取该锁所有线程的。在“公平锁”的机制下,线程依次排队获取锁;而“非公平锁”在锁是可获取状态时,不管自己是不是在队列的开头都会获取锁。

ReentrantLock构造方法

   /**
     * 默认创建非公平锁
     */
    public ReentrantLock() {
        sync = new NonfairSync();
    }

    /**
     * fair为true表示是公平锁,fair为false表示是非公平锁
     */
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

ReentrantLock核心方法

   /**
     * 阻塞等待获取锁;不允许Thread.interrupt中断,即使检测到Thread.isInterrupted,一样会继续尝试      * 获取锁,失败则继续休眠。只是在最后获取锁成功后再把当前线程置为interrupted状态,然后再中断线程。
     */
    public void lock() {
        sync.lock();
    }

    /**
     * 当前线程未被中断,则获取锁
     * 允许在等待时由其它线程调用等待线程的Thread.interrupt方法来中断等待线程的等待而直接返回,这时      * 不用获取锁,而会抛出一个InterruptedException
     */
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }

    /**
     *尝试申请一个锁,在成功获得锁后返回true,否则,立即返回false
     */
    public boolean tryLock() {
        return sync.nonfairTryAcquire(1);
    }

    /**
     * 在一段时间内尝试申请一个锁,在成功获得锁后返回true,否则,立即返回false
     */
    public boolean tryLock(long timeout, TimeUnit unit)
            throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    }

    /**
     * 释放锁
     */
    public void unlock() {
        sync.release(1);
    }

    /**
     * 条件实例
     */
    public Condition newCondition() {
        return sync.newCondition();
    }

    /**
     * 获取当前线程持有此锁的次数
     */
    public int getHoldCount() {
        return sync.getHoldCount();
    }

    /**
     * 是否被当前线程持有
     */
    public boolean isHeldByCurrentThread() {
        return sync.isHeldExclusively();
    }

    /**
     * 查询此锁是否由任意线程持有
     */
    public boolean isLocked() {
        return sync.isLocked();
    }

    /**
     *如果是“公平锁”返回true,否则返回false
     */
    public final boolean isFair() {
        return sync instanceof FairSync;
    }

    /**
     * 获取目前拥有此锁的线程,如果此锁不被任何线程拥有,则返回 null
     */
    protected Thread getOwner() {
        return sync.getOwner();
    }

    /**
     * 查询是否有线程正在等待
     */
    public final boolean hasQueuedThreads() {
        return sync.hasQueuedThreads();
    }

    /**
     *查询给定线程是否正在等待获取此锁。
     */
    public final boolean hasQueuedThread(Thread thread) {
        return sync.isQueued(thread);
    }

    /**
     * 获取正等待获取此锁的线程数
     */
    public final int getQueueLength() {
        return sync.getQueueLength();
    }
   /**
     * 正等待获取此锁的线程集合
     */
    protected Collection<Thread> getQueuedThreads() {
        return sync.getQueuedThreads();
    }
 /**
     *是否存在正在等待并符合相关给定条件的线程
     */
    public boolean hasWaiters(Condition condition) {
        if (condition == null)
            throw new NullPointerException();
        if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
            throw new IllegalArgumentException("not owner");
        return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition);
    }

    /**
     * 正在等待并符合相关给定条件的线程数量
     */
    public int getWaitQueueLength(Condition condition) {
        if (condition == null)
            throw new NullPointerException();
        if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
            throw new IllegalArgumentException("not owner");
        return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition);
    }

    /**
     * 正在等待并符合相关给定条件的线程集合
     */
    protected Collection<Thread> getWaitingThreads(Condition condition) {
        if (condition == null)
            throw new NullPointerException();
        if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
            throw new IllegalArgumentException("not owner");
        return sync.getWaitingThreads((AbstractQueuedSynchronizer.ConditionObject)condition);
    }

ReentrantLock和Synchronized比较

  1. synchronized是独占锁,加锁和解锁的过程自动进行,易于操作,但不够灵活。ReentrantLock也是独占锁,加锁和解锁的过程需要手动进行,不易操作,但非常灵活。
  2. synchronized可重入,因为加锁和解锁自动进行,不必担心最后是否释放锁;ReentrantLock也可重入,但加锁和解锁需要手动进行,且次数需一样,否则其他线程无法获得锁。
  3. synchronized不可响应中断,一个线程获取不到锁就一直等着;ReentrantLock可以相应中断。

condition

  • 用来替代传统的Object的wait()、notify()实现线程间的协作,相比使用Object的wait()、notify(),使用Condition的await()、signal()这种方式实现线程间协作更加安全和高效。

应用场景

生产者和消费者

public class Consumer implements Runnable {
    private List<Integer> container;
    private ReentrantLock lock;
    private Condition producerCondition;
    private Condition consumerCondition;

    public Consumer(List<Integer> container, ReentrantLock lock, Condition producerCondition, Condition consumerCondition) {
        this.container = container;
        this.lock = lock;
        this.producerCondition = producerCondition;
        this.consumerCondition = consumerCondition;
    }

    public void consumer() {
        try {
            //获得锁
            lock.lock();
            //容器空了,需要生产
            if (container.size() == 0) {
                System.out.println("消费完了,。。。。");
                consumerCondition.await();

            }
            Integer p = container.remove(0);
            //模拟1秒消费一个产品
            TimeUnit.MILLISECONDS.sleep(1000);
            System.out.println("消费产品:" + p);
            //消费一个产品,通知生产者
            producerCondition.signal();

        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //释放锁
            lock.unlock();
        }
    }

    @Override
    public void run() {
        while (true) {
            consumer();
        }
    }

    public static void main(String[] args) {
        List<Integer> container = new ArrayList<>(5);
        ReentrantLock lock = new ReentrantLock();
        Condition producerCondition = lock.newCondition();
        Condition consumerCondition = lock.newCondition();
        Thread threadproduce = new Thread(new Producer(container, lock, producerCondition, consumerCondition));
        Thread threadconsumer = new Thread(new Consumer(container, lock, producerCondition, consumerCondition));
        threadproduce.start();
        threadconsumer.start();
    }
}
public Producer(List<Integer> container, ReentrantLock lock, Condition producerCondition, Condition consumerCondition) {
        this.container = container;
        this.lock = lock;
        this.producerCondition = producerCondition;
        this.consumerCondition = consumerCondition;
    }
    public void produce() {
        //产品容器容量大小
        int capacity = 5;
        try {
            //获得锁
            lock.lock();
            //容器满了,不在生产
            if (container.size() == capacity) {
                System.out.println("生产满了。。。。");
                producerCondition.await();
            }
            Random random = new Random();
            int p = random.nextInt(50);
            //模拟1秒生产一个产品
            TimeUnit.MILLISECONDS.sleep(1000);
            System.out.println("生产产品:" + p);
            container.add(p);
            //生产一个产品,通知消费者
            consumerCondition.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //释放锁
            lock.unlock();
        }

    }

    @Override
    public void run() {
        while (true){
            produce();
        }

    }

顺序打印ABC十遍

public class PrintABC {
    private static ReentrantLock lock = new ReentrantLock();
    private static Condition conditionA = lock.newCondition();
    private static Condition conditionB = lock.newCondition();
    private static Condition conditionC = lock.newCondition();
    private static volatile int state = 1;

    public static void main(String[] args) throws InterruptedException {

        ExecutorService poolService = Executors.newFixedThreadPool(3);
        Integer count = 10;
        poolService.execute(new Worker("A", count, 1, lock, conditionA, conditionB));
        poolService.execute(new Worker("B", count, 2, lock, conditionB, conditionC));
        poolService.execute(new Worker("C", count, 3, lock, conditionC, conditionA));
        Thread.sleep(5000);
        poolService.shutdownNow();
    }

    public static class Worker implements Runnable {
        private String key;
        private Integer count;
        private Lock lock;
        private Condition current;
        private Condition next;
        private int targetState;

        public Worker(String key, Integer count, int targetState, Lock lock, Condition cur, Condition next) {
            this.key = key;
            this.count = count;
            this.lock = lock;
            this.current = cur;
            this.next = next;
            this.targetState = targetState;
        }

        @Override
        public void run() {
            this.lock.lock();
            try {
                for (int i = 0; i < count; i++) {
                    while (state != targetState) {
                        current.await();
                        System.out.println(Thread.currentThread().getName()+"释放对象锁");
                    }
                    System.out.println(i + "," + key);
                    state++;
                    if (state > 3) {
                        state = 1;
                    }
                    next.signal();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                this.lock.unlock();
            }
        }
    }
}

lock方法分析

ReenTrantLock的锁有两种实现方式:公平锁和非公平锁。两者都继承了Sync,Sync又继承了AbstractQueuedSynchronizer(AQS)。下面我们对lock方法做一个解析

//非公平锁
static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

        /**
         * Performs lock.  Try immediate barge, backing up to normal
         * acquire on failure.
         */
        final void lock() {
            //利用CAS进行比较和交换,在AbstractQueuedSynchronizer中通过state值判断锁是否持有,             //如果是1则锁持有,0表示锁释放。如果state>1表示一个锁被同一个线程持有多次,这里就是可重             //入的实现。
            if (compareAndSetState(0, 1))
                //设置状态值成功后则将当前线程设置拥有独占锁
                setExclusiveOwnerThread(Thread.currentThread());
            else
                //如果当前锁已被其他线程持有,则调用acuire方法
                acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }

public final void acquire(int arg) {
        //再次尝试获取锁,如果还是没有获取到则加入到双向链表的尾部,并通过自旋等待获取锁,在一定条件下会停止自旋并中断当前线程
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            //设置当前线程中断
            selfInterrupt();
    }
//公平锁,唯一的区别是这里不直接先通过CAS去获取锁
static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

        final void lock() {
            acquire(1);
        }

补充:

  • AQS:基于CLH队列,用volatile修饰共享变量state,线程通过CAS去改变状态符,成功则获取锁成功,失败则进入等待队列,等待被唤醒。CLH队列是FIFO的双端双向队列。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值