zoukankan      html  css  js  c++  java
  • 互斥锁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的使用,原理部分会另开一偏文章进行讲解。

  • 相关阅读:
    班会记录
    CSS之伪元素
    JavaScript之返回顶部
    尝试Hexo
    GitHub之上传文件
    Git之使用
    Git之基本命令
    运行第一个Node.js程序
    go语言圣经 map 章节习题
    go语言圣经第4章JSON部分习题
  • 原文地址:https://www.cnblogs.com/dpains/p/7494521.html
Copyright © 2011-2022 走看看