互斥锁ReentrantLock

概述


1.ReentrantLock简介

2.ReentrantLock示例

3.ReentrantLock与Condition示例

ReentrantLock简介

ReentrantLock是一个可重入的互斥锁,又称独占锁;

即ReentrantLock是同一时间点只能被一个线程持有,可重入的意思就是持有该锁的线程可再次获取锁。

ReentrantLock分为公平和非公平锁。它们的区别体现在获取锁的机制上是否公平。锁是为了保护竞争资源,防止多个线程同时操作而出错。ReentrantLock在同一时间只能被一个线程获取,在某个线程获取时,其他线程就必须等待。ReentrantLock是通过一个FIFO的队列来管理获取该锁的线程的,在公平锁的情况下,依次排队获取;在非公平锁的情况下,在锁是可获取状态时,不管是不是队列的开头都会获取锁。

ReentrantLock示例

还是以生产者消费者的场景来举例,接下来通过两个示例还对比下锁存在和不存在的情况:

示例1:无锁的情况,生产者往仓库生成,消费者从仓库消费,操作完后,sleep一小会;

//仓库的代码
public
class Depot { private int size; private int maxSize=50; private Condition prodCondition; private Lock lock; public Depot(){ this.size=0; this.lock=new ReentrantLock(); } public void prod(int val){ try{ size+=val; try { Thread.sleep((long)val); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+" produce "+val+" total size is "+size); } } public void consum(int val){ try{ size-=val; try { Thread.sleep((long)val); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+" consumer "+val+" total size is "+size); } } }

测试代码:

public class ReentrantLockTest {


    public static void main(String[] args){

//测试生产者消费者 Depot depot
=new Depot(); new ProducerLock(depot,10).start(); new ConsumerLock(depot,20).start(); new ProducerLock(depot,30).start(); new ProducerLock(depot,20).start(); new ProducerLock(depot,20).start(); new ConsumerLock(depot,70).start(); } } class ProducerLock extends Thread{ Depot depot; int val; public ProducerLock(Depot depot,int val){ this.depot=depot; this.val=val; } public void run(){ depot.prod(val); } } class ConsumerLock extends Thread{ Depot depot; int val; public ConsumerLock(Depot depot,int val){ this.depot=depot; this.val=val; } public void run(){ depot.consum(val); } }

输出结果: 可以看到多个线程对同一仓库进行操作,结果完全乱套了

Thread-0 produce 10 total size is -10
Thread-3 produce 20 total size is -10
Thread-4 produce 20 total size is -10
Thread-1 consumer 20 total size is -10
Thread-2 produce 30 total size is -10
Thread-5 consumer 70 total size is -10

示例2,现在看下加锁后的情况,即在Depot类中将prod和consum方法里面的操作分别加上lock,以及在finally里释放lock,如下:

public class Depot {

    private int size;
    private int maxSize=50;

    private Lock lock;
    public Depot(){

        this.size=0;
        this.lock=new ReentrantLock();
    }

    public void prod(int val){

        lock.lock();

        try{
            size+=val;
            try {
                Thread.sleep((long)val);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+" produce "+val+" total size is "+size);

        }finally {

            lock.unlock();
        }

    }

    public void consum(int val){

        lock.lock();
        try{

            size-=val;
            try {
                Thread.sleep((long)val);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+" consumer "+val+" total size is "+size);
        }finally {

            lock.unlock();
        }
    }
}

输出结果如下:可以看到多个线程进行操作时,没有出现混乱的情况,但是结果还是有负数,正常来讲仓库满了就不生成了,仓库为空就不让消费了,怎么解决呢?

结合Condition来解决 

Thread-0 produce 10 total size is 10
Thread-1 consumer 20 total size is -10
Thread-2 produce 30 total size is 20
Thread-3 produce 20 total size is 40
Thread-4 produce 20 total size is 60
Thread-5 consumer 70 total size is -10

ReentrantLock与Condition示例

为解决上面的问题,在lock中引入Condition,设置两个Condition,生成者在仓库满时,进入等待状态,同时唤醒消费者线程,消费者在仓库为空时,进入等待。同时唤醒生产者线程。

public class Depot {

    private int size;
    private int maxSize=50;
//    private int count;
    private Condition prodCondition;
    private Condition consumCondition;

    private Lock lock;
    public Depot(){

        this.size=0;
        this.lock=new ReentrantLock();
        this.prodCondition=this.lock.newCondition();
        this.consumCondition=this.lock.newCondition();
    }

    public void prod(int val){

        lock.lock();

        try{

            while(size+val>maxSize){
 //如果生产超过max值,则生产者进入等待
                try {
                    prodCondition.await();
                    System.out.println(Thread.currentThread().getName()+" is awaiting");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            size+=val;
            try {
                Thread.sleep((long)val);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+" produce "+val+" total size is "+size);
            consumCondition.signal(); //唤醒消费者线程


        }finally {

            lock.unlock();
        }

    }

    public void consum(int val){

        lock.lock();
        try{

            while(size-val<0){
//如果当前大小减去要消费的值,如果小于0的话,则进入等待
                try {
                    consumCondition.await();
                    System.out.println(Thread.currentThread().getName()+" is awaiting");

                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            size-=val;
            try {
                Thread.sleep((long)val);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+" consumer "+val+" total size is "+size);
            prodCondition.signal(); //唤醒生产者线程
        }finally {

            lock.unlock();
        }
    }
}

测试结果如下:可以看到没有负数的情况,也没有仓库大小超过50的情况,还能看到线程进入等待的情况。

Thread-0 produce 10 total size is 10
Thread-2 produce 30 total size is 40
Thread-1 consumer is awaiting
Thread-1 consumer 20 total size is 20
Thread-3 producer is awaiting
Thread-3 produce 20 total size is 40
Thread-5 consumer is awaiting

这里只讲解了ReentrantLock和condition的使用,原理部分会另开一偏文章进行讲解。

原文地址:https://www.cnblogs.com/dpains/p/7494521.html