Java:保证线程安全的加锁机制之ReentrantLock

前言

   在保证线程安全的机制 同步阻塞 中,加锁方式除了synchronized还有一个不同于它的,需要手动加锁解锁的方式:ReentrantLock类的实现。

一、介绍

    ReentrantLock是jdk1.5新增的和synchronized一样可以达到同步互斥效果的一个可重入锁,它接口自Lock类。(可重入锁:可重复可递归调用的锁,在外层使用完锁之后,在内层仍然可以使用)

二、Lock接口

   该接口是无条件的、可轮询的、定时的、可中断的锁获取操作。其中方法包括:
(1)lock():该方法用来获取锁,如果锁已经被其他线程获取,进行等待。
(2)lockInterruptibly():该方法用来获取锁,如果锁已经被其他线程获取,进行等待。等待途中允许其他线程调用interrupt()方法来中断等待,并抛出InterruptedException。
(3)tryLock():该方法用来获取锁,如果获取成功返回true,失败则返回false。
(4)tryLock(long time,TimeUnit unit):该方法用来获取锁,如果锁已经被其他线程获取,等待time长的时间,在时限内获取到锁就会返回true,时限过后还获取不到就会返回false
(5)unlock():该方法用来释放锁

三、实现

以一个简单的生消模型作为例子:

class SharingQue<E>{
    private final LinkedList<E> queue = new LinkedList<>();
    private static final int defaultMax = 5;
    private final int capacity;

    private ReentrantLock lock = new ReentrantLock();
    private Condition isFull = lock.newCondition();//两个阻塞队列
    private Condition isEmpty = lock.newCondition();

    public SharingQue(){
        this(defaultMax);
    }

    public SharingQue(int size){
        this.capacity = size;
    }

    public void put(E value){
        lock.lock();
        try {
            while(queue.size() >= capacity){
                System.out.println(Thread.currentThread().getName()+":位子满了,放不进去");
                isFull.await();
            }
            queue.addLast(value);
            System.out.println(Thread.currentThread().getName()+":放入"+value);
            isEmpty.signalAll();//让那些因为队列空拿不到东西的消费者们竞争出一个来拿
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }

    }

    public void take(){
        lock.lock();
        try {
            while(queue.isEmpty()){
                System.out.println(Thread.currentThread().getName()+":没有东西,无法消费");
                isEmpty.await();
            }
            E thing = queue.removeFirst();
            System.out.println(Thread.currentThread().getName()+":拿走"+thing);
            isFull.signalAll();//因没位子而阻塞生产者们竞争出一个可以来放了
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }

    }
}

可以看出:
(1)Condition对象可以用来当做阻塞队列,并且个数不再囿于一个,可以用多个阻塞队列来存放因不同原因阻塞的线程
(2)加锁解锁必须要手动进行,并且unlock()有可能还执行不到,因为finally有时也会被忘记,这是他的一个缺陷。
(3)在put()写操作完成后,因队列空而阻塞住的消费者就可以来竞争着消费了,用notifyAll()会更加公平,也会避免死锁问题的发生;在take()读操作完成后,因队列满而阻塞住的生产者也可以来竞争着放产品了。

四、原理

看源码:首先,根据Sync类(继承至AbstractQueuedSynchronizer类)创建一个同步控制的基础sync,后面分成不公平和公平两个版本:NonfairSync和FairSync,两者都是ReentrantLock的静态内部类,而ReentrantLock的很多方法也都是它们代为实现的。
在这里插入图片描述
在构造函数中:如果new ReentrantLock()不传参,就默认创建一个不公平排序策略的对象,即直接用CAS占有锁,不管有没有其他等待的线程;如果传了boolean参数,参数是true就创建一个公平排序策略的对象,false就创建不公平排序策略对象。

而调用方法的时候,就根据sync是FairSync还是NonFairSync的对象来调用不同的方法。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值