Reentrantlock简介及使用例子(生产者消费者)

1 篇文章 0 订阅
1 篇文章 0 订阅

目录

一、基本使用方法

二、ReentrantLock类

三、其他注意

四、应用与实现(生产者消费者模式) 


一、基本使用方法

ReentrantLock是Java并发包中互斥锁,它有公平锁和非公平锁两种实现方式,以lock()为例,其使用方式为:

ReentrantLock takeLock = new ReentrantLock(); 
   
// 获取锁 
takeLock.lock(); 
   
try { 
     
  // 业务逻辑 
     
} finally { 
  // 释放锁 
  takeLock.unlock(); 
} 

那么,ReentrantLock内部是如何实现锁的呢?接下来我们就以JDK1.7中的ReentrantLock的lock()方法叙述。

二、ReentrantLock类

 ReentrantLock类实现了Lock和java.io.Serializable接口,其内部有一个实现锁功能的关键成员变量Sync类型的sync,定义如下:

/** Synchronizer providing all implementation mechanics */ 
private final Sync sync; 

    锁类型:  NonfairSync、FairSync,正好对应了ReentrantLock的非公平锁、公平锁两大类型。ReentrantLock默认是公平锁的实现。
 

 

/**
 * Creates an instance of {@code ReentrantLock}.
 * This is equivalent to using {@code ReentrantLock(false)}.
 */ 
public ReentrantLock() { 
    sync = new NonfairSync(); 
} 

当然我们可以通过另一个构造方法实现兼容的方式:

/**
 * Creates an instance of {@code ReentrantLock} with the
 * given fairness policy.
 *
 * @param fair {@code true} if this lock should use a fair ordering policy
 */ 
public ReentrantLock(boolean fair) { 
    sync = fair ? new FairSync() : new NonfairSync(); 
} 

ReentrantLock的lock()方法会调用其内部成员变量sync的lock()方法;

        其次,sync的非公平锁NonfairSync或公平锁FairSync实现了父类AbstractQueuedSynchronizer的lock()方法,其会调用acquire()方法;

        然后,acquire()方法则在sync父类AbstractQueuedSynchronizer中实现,它只有一段代码:

 

public final void acquire(int arg) { 
    if (!tryAcquire(arg) && 
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) 
        selfInterrupt(); 
} 

tryAcquire()方法试图获取锁,获取到直接返回结果,否则就通过嵌套调用acquireQueued()、addWaiter()方法将请求获取锁的线程加入等待队列,等到成功的时候,将当前请求线程阻塞,接着就执行结束!

而针对每种锁来说,其实现方式有很大差别,主要就体现在各自实现类的lock()和tryAcquire()方法中。在sync的抽象类Sync及其抽象父类AQS中,lock()方法和tryAcquire()方法被定义为抽象方法或者未实现,而是由具体子类去实现:

  • 非公平锁NonfairSync的具体实现

          lock方法:
 

/**
 * Performs lock.  Try immediate barge, backing up to normal
 * acquire on failure.
 */ 
final void lock() { 
    if (compareAndSetState(0, 1)) 
        setExclusiveOwnerThread(Thread.currentThread()); 
    else 
        acquire(1); 
} 

       非公平锁上来就无视等待队列的存在而抢占锁,通过基于CAS操作的compareAndSetState(0, 1)方法,试图修改当前锁的状态,这个0表示AbstractQueuedSynchronizer内部的一种状态,针对互斥锁则是尚未有线程持有该锁,而>=1则表示存在线程持有该锁,并重入对应次数,这个上来就CAS的操作也是非公共锁的一种体现,CAS操作成功的话,则将当前线程设置为该锁的唯一拥有者。抢占不成功的话,则调用父类的acquire()方法,由上所述,继而调用tryAcquire()方法,这个方法也是由最终实现类NonfairSync实现的,如下:

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

nonfairTryAcquire()方法实现如下:

 

 

/**
 * Performs non-fair tryLock.  tryAcquire is
 * implemented in subclasses, but both need nonfair
 * try for trylock method.
 */ 
final boolean nonfairTryAcquire(int acquires) { 
    final Thread current = Thread.currentThread(); 
    int c = getState(); 
    if (c == 0) { 
        if (compareAndSetState(0, acquires)) { 
            setExclusiveOwnerThread(current); 
            return true; 
        } 
    } 
    else if (current == getExclusiveOwnerThread()) { 
        int nextc = c + acquires; 
        if (nextc < 0) // overflow 
            throw new Error("Maximum lock count exceeded"); 
        setState(nextc); 
        return true; 
    } 
    return false; 
}

 这段代码先判断锁的状态,通过CAS来抢占,抢占成功,直接返回true,如果锁的持有者线程为当前线程的话,则通过累加状态标识重入次数。抢占不成功,或者锁的本身持有者不是当前线程,则返回false,继而后续通过进入等待队列的方式排队获取锁。

  • 公平锁FairSync的具体实现
    lock方法:
    final void lock() { 
        acquire(1); 
    } 

    公平锁的tryAcquire()方法也相对较简单,如下:

    /**
     * Fair version of tryAcquire.  Don't grant access unless
     * recursive call or no waiters or is first.
     */ 
    protected final boolean tryAcquire(int acquires) { 
        final Thread current = Thread.currentThread(); 
        int c = getState(); 
        if (c == 0) { 
            if (!hasQueuedPredecessors() && 
                compareAndSetState(0, acquires)) { 
                setExclusiveOwnerThread(current); 
                return true; 
            } 
        } 
        else if (current == getExclusiveOwnerThread()) { 
            int nextc = c + acquires; 
            if (nextc < 0) 
                throw new Error("Maximum lock count exceeded"); 
            setState(nextc); 
            return true; 
        } 
        return false; 
    }

当前线程会在得到当前锁状态为0,即没有线程持有该锁,并且通过!hasQueuedPredecessors()判断当前等待队列没有前继线程(也就是说,没有比我优先级更高的线程在请求锁了)获取锁的情况下,通过CAS抢占锁,并设置自己为锁的当前拥有者,当然,如果是重入的话,和非公平锁处理一样,通过累加状态位标记重入次数。

        而一旦等待队列中有等待者,或当前线程抢占锁失败,则它会乖乖的进入等待队列排队等待。

三、其他注意
 

即便是公平锁,如果通过不带超时时间限制的tryLock()的方式获取锁的话,它也是不公平的,因为其内部调用的是sync.nonfairTryAcquire()方法,无论抢到与否,都会同步返回。如下:

public boolean tryLock() { 
    return sync.nonfairTryAcquire(1); 
}

但是带有超时时间限制的tryLock(long timeout, TimeUnit unit)方法则不一样,还是会遵循公平或非公平的原则的,如下:

public boolean tryLock(long timeout, TimeUnit unit) 
        throws InterruptedException { 
    return sync.tryAcquireNanos(1, unit.toNanos(timeout)); 
} 

四、应用与实现(生产者消费者模式) 

1、先定义一个仓库接口,用于消费和存储:

/**
 * Created by fx on 2019/1/15.
 */
public interface StoreInterface {
    void produce(int number);
    void consume(int number);
}

2、实现消费工厂的消费功能和生成工厂的生成功能,并加上锁:

当消费者消费的数量不足时,此时仓库是不可以支出库存的,所以用到reentrantLock.lock(),等待生产者的生产,执行condition.wait()等待。循环等待,当库存量大于需求量,则唤醒当前的消费者,condition.signalAll(),这里也可以用两个condition分别控制消费者和生产者,这里为了方便,只用一个condition控制两个的执行,但是一个condition只能针对于一个线程任务。

生产者同理,当生产量大于仓库容纳总量就会等待,停止生产,知道消费者消费到一定的库存量,让他能继续生产放入仓库。

import java.util.LinkedList;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created by fx on 2019/1/15.
 */
public class ReentrantlockStore implements StoreInterface{

    private LinkedList linkList = new LinkedList();

    private static int Max_Value = 100;
    private int number;
    private ReentrantLock reentrantLock = new ReentrantLock();
    private Condition condition = reentrantLock.newCondition();


    public void produce(int number){
        reentrantLock.lock();
        try{
            while(linkList.size()+number>Max_Value){
                System.out.println("【要生产的产品数量】:" + number + "\t【库存量】:"+ linkList.size() + "\t暂时不能执行生产任务!");
                try {
                    condition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            reentrantLock.unlock();
        }
        for (int i=0;i<number;i++ ){
            linkList.add(new Object());
        }

        System.out.println("【已经生产产品数】:" + number + "\t【现仓储量为】:" + linkList.size());
        condition.signalAll();
    }

    public void consume(int number){
        reentrantLock.lock();
        try{
            while (linkList.size()<number){
                System.out.println("【要消费的产品数量】:" + number + "\t【库存量】:"+ linkList.size() + "\t暂时不能执行消费任务!");
                try {
                    condition.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            reentrantLock.unlock();
        }

        for (int i=0;i<number;i++ ){
            linkList.remove();
        }

        System.out.println("【已经消费产品数】:" + number + "\t【现仓储量为】:" + linkList.size());

        condition.signalAll();
    }
}

 

3、定义消费者工厂:

/**
 * Created by fx on 2019/1/15.
 */
public class ConsumerFactory extends Thread {

    private int number;
    private StoreInterface  storeHouse;

    public ConsumerFactory(int number,StoreInterface storeHouse){
        this.number =number;
        this.storeHouse = storeHouse;
    }

    public void  consume(int number){
        storeHouse.consume(number);
    }

    @Override
    public void run() {
        consume(number);
    }
}

4、定义生成者工厂:
 

/**
 * Created by fx on 2019/1/15.
 */
public class ProductFactory extends Thread{

    private int number;
    private StoreInterface storeHouse;

    public ProductFactory(int number,StoreInterface storeHouse){
        this.number = number;
        this.storeHouse = storeHouse;
    }

    @Override
    public void run() {
        produce(number);
    }

    public void produce(int number){
        storeHouse.produce(number);
    }
}

 5、最后的执行主函数,来确定消费者和生产者:这里定义了十个线程,消费者每次消费五个,生产者每次生产三个,库存总量是最大100.

/**
 * Created by fx on 2019/1/15.
 */
public class MainDemo {

    public static void main(String[] args) {
        StoreHouse storeHouse = new StoreHouse();
        for(int i =0;i<10;i++){
            ThreadPoolExecutorUtil.threadPool.submit(new ConsumerFactory(5,storeHouse));
            ThreadPoolExecutorUtil.threadPool.submit(new ProductFactory(3,storeHouse));

        }

        System.out.println("**************分割线****************");
        ReentrantlockStore reentrantlockStore = new ReentrantlockStore();
        for(int i =0;i<10;i++){
            ThreadPoolExecutorUtil.threadPool.submit(new ConsumerFactory(5,reentrantlockStore));
            ThreadPoolExecutorUtil.threadPool.submit(new ProductFactory(3,reentrantlockStore));
        }


    }
}

最后结果显示:

以上就是Reentrantlock的基本讲解和用法,想了解更多,可以查看源码。当然也要记得看他的父类AQS哦,这里也是相当有趣呢。有时间我也会写一篇关于AQS的讲解。

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值