美文网首页
可重入锁 ReentranLock 和 Condition

可重入锁 ReentranLock 和 Condition

作者: Ethan_Walker | 来源:发表于2018-05-11 18:48 被阅读41次

    ReentrantLock 可重用锁

    1. 同一个线程未释放锁之前获取同一把锁

    一个线程在获取了锁之后,再次去获取了同一个锁,这时候仅仅是把使用计数 +1

    package lock9_method.holdCount;
    
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * Created by lenovo on 2018/5/10.
     */
    public class Service {
        public ReentrantLock lock = new ReentrantLock();
    
        public void method() {
            try {
                lock.lock();
                System.out.println(lock.getHoldCount());
                Thread.sleep(1000);
                method2();
    
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
        public void method2(){
            lock.lock();
            System.out.println(lock.getHoldCount());
            lock.unlock();
    
        }
    }
    package lock9_method.holdCount;
    
    /**
     * Created by lenovo on 2018/5/10.
     */
    public class Main {
        public static void main(String[] args) {
            Service service = new Service();
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    for(int i=0;i<10;i++){
                        service.method();
                    }
                }
            };
    
            Thread thread = new Thread(runnable);
            thread.start();
    
            Thread countThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    while(true){
                        try {
                            Thread.sleep(2000);
                            //当前线程拥有 该锁的计数,在这调用肯定为 0
                            System.out.println("当前锁被线程a 持有计数:"+service.lock.getHoldCount());
                        } catch (InterruptedException e) {
    
                            e.printStackTrace();
                        }
                    }
                }
            });
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            countThread.start();
    
    
        }
    }
    
    
    

    输出:

    1
    2
    1
    2
    1
    2
    1
    2
    1
    当前锁被线程a 持有计数:0
    2
    1
    2
    1
    当前锁被线程a 持有计数:0
    

    2. 使用 ReentrantLock 、Condition演示简单等待唤醒机制

    package lock2;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * Created by lenovo on 2018/5/3.
     */
    public class MyService206 {
        private ReentrantLock lock = new ReentrantLock();
        private Condition condition = lock.newCondition();
    
        public void waitMethod() {
            try {
                lock.lock();
                for (int i = 0; i < 3; i++) {
                    System.out.println("wait:" + i);
                }
                condition.await();
                for (int i = 4; i < 6; i++) {
                    System.out.println("wait:" + i);
                }
                lock.unlock();
                System.out.println("wait方法中锁被释放");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
        }
    
        public void signalMethod() {
            lock.lock();
            for (int i = 0; i < 3; i++) {
                System.out.println("notify:" + i);
            }
            condition.signal();
            for (int i = 4; i < 6; i++) {
                System.out.println("notify:" + i);
            }
            lock.unlock();
            System.out.println("notify 方法中锁被释放");
    
        }
    
    }
    package lock2;
    
    /**
     * Created by lenovo on 2018/5/3.
     */
    public class ThreadA extends Thread {
        private MyService206 myService206;
    
        public ThreadA(MyService206 myService206) {
            this.myService206 = myService206;
        }
    
        @Override
        public void run() {
            myService206.waitMethod();
        }
    }
    package lock2;
    
    /**
     * Created by lenovo on 2018/5/3.
     */
    public class ThreadB extends Thread {
        private MyService206 myService206;
    
        public ThreadB(MyService206 myService206) {
            this.myService206 = myService206;
        }
    
        @Override
        public void run() {
            myService206.signalMethod();
        }
    }
    package lock2;
    
    /**
     * 使用 Condition实现简单等待唤醒机制
     * Created by lenovo on 2018/5/3.
     */
    public class Run206 {
        public static void main(String[] args) {
            MyService206 myService206 = new MyService206();
            ThreadA threadA = new ThreadA(myService206);
            threadA.start();
    
            ThreadB threadB = new ThreadB(myService206);
            threadB.start();
    
        }
    }
    
    
    

    输出:

    wait:0
    wait:1
    wait:2
    notify:0
    notify:1
    notify:2
    notify:4
    notify:5
    wait:4
    wait:5
    wait方法中锁被释放
    notify 方法中锁被释放
    

    3. 同一把锁、多个 Condition 实现唤醒 指定等待线程

    package lock4;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * Created by lenovo on 2018/5/3.
     */
    public class MyService {
    
        ReentrantLock lock = new ReentrantLock();
        private Condition conditionA = lock.newCondition();
        private Condition conditionB = lock.newCondition();
    
        public void awaitA() {
            try {
                lock.lock();
                for (int i = 0; i < 2; i++) {
                    System.out.println("awaitA: " + i);
                }
                conditionA.await();
                for (int i = 3; i < 5; i++) {
                    System.out.println("awaitA: " + i);
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
                System.out.println("awaitA unlock");
            }
    
        }
    
        public void awaitB() {
            try {
                lock.lock();
                for (int i = 0; i < 2; i++) {
                    System.out.println("awaitB: " + i);
                }
                conditionB.await();
                for (int i = 3; i < 5; i++) {
                    System.out.println("awaitB: " + i);
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
                System.out.println("awaitB unlock");
            }
    
        }
    
        public void signalAll_A() {
            try {
                lock.lock();
                conditionA.signalAll();
                System.out.println("线程 A 被唤醒");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
    
        }
    
        public void signalAll_B() {
            try {
                lock.lock();
                conditionB.signalAll();
                System.out.println("线程 B 被唤醒");
    
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
    
            }
    
        }
    }
    package lock4;
    
    /**
     * Created by lenovo on 2018/5/3.
     */
    public class ThreadA extends Thread {
        private MyService myService;
    
        public ThreadA(MyService myService) {
            this.myService = myService;
        }
    
        @Override
        public void run() {
            myService.awaitA();
        }
    }
    package lock4;
    
    /**
     * Created by lenovo on 2018/5/3.
     */
    public class ThreadB extends Thread {
        private MyService myService;
    
        public ThreadB(MyService myService) {
            this.myService = myService;
        }
    
        @Override
        public void run() {
            myService.awaitB();
        }
    }
    package lock4;
    
    /**
     * Created by lenovo on 2018/5/3.
     * 使用多个 Condition 实现通知指定 等待线程
     *
     */
    public class Run212 {
        public static void main(String[] args) {
            MyService myService = new MyService();
            ThreadA threadA = new ThreadA(myService);
            ThreadB threadB = new ThreadB(myService);
    
            threadA.start();
            threadB.start();
            try {
                Thread.sleep(4000);
                myService.signalAll_A();
    
                Thread.sleep(3000);
                myService.signalAll_B();
    
            } catch (Exception e) {
                e.printStackTrace();
            }
    
    
        }
    }
    
    

    输出

    awaitB: 0
    awaitB: 1
    awaitA: 0
    awaitA: 1
    线程 A 被唤醒
    awaitA: 3
    awaitA: 4
    awaitA unlock
    线程 B 被唤醒
    awaitB: 3
    awaitB: 4
    awaitB unlock
    

    4. 使用 ReentrantLock 和 Condition实现生产者消费者模式

    4.1 缓存中只支持一个值

    package lock5_producer;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * Created by lenovo on 2018/4/23.
     */
    public class MyService213 {
        private ReentrantLock reentrantLock = new ReentrantLock();
        private Condition condition = reentrantLock.newCondition();
    
        private boolean hasValue = false;
    
        /**
         * 生产者
         */
        public void set() {
            try {
                reentrantLock.lock();
    
                while (hasValue == true) {
                    // 有值,生产者线程等待
                    condition.await();
                }
                // 无值
                System.out.println("生产者在生产...");
                hasValue = true;
    
                condition.signal();  // 生产之后 唤醒 消费者
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                reentrantLock.unlock();
            }
        }
    
        /**
         * 消费者
         */
    
        public void get() {
            try {
                reentrantLock.lock();
                while (hasValue == false) {
                    // 无值,消费者在等待
                    condition.await();
                }
                System.out.println("消费者在消费");
                hasValue = false;
                condition.signal();  // 消费完之后唤醒生产者
    
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                reentrantLock.unlock();
            }
        }
    
    }
    package lock5_producer;
    
    /**
     * Created by lenovo on 2018/4/23.
     */
    public class MyThreadA extends Thread {
        private MyService213 myService213;
    
        public MyThreadA(MyService213 myService213) {
            this.myService213 = myService213;
        }
    
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                myService213.get();
            }
        }
    
    
    }
    package lock5_producer;
    
    /**
     * Created by lenovo on 2018/4/23.
     */
    public class MyThreadB extends Thread {
        private MyService213 myService213;
    
        public MyThreadB(MyService213 myService213){
            this.myService213 = myService213;
        }
    
        @Override
        public void run() {
            for(int i=0;i<100;i++){
                myService213.set();
            }
        }
    }
    package lock5_producer;
    
    /**
     * Created by lenovo on 2018/4/23.
     * ReentrantLock 、Condition 实现生产者消费者模式(缓存中数据最大值为 1)
     */
    public class Run213 {
        public static void main(String[] args) {
            MyService213 myService213 = new MyService213();
            MyThreadA myThreadA = new MyThreadA(myService213);
            myThreadA.start();
    
            MyThreadB myThreadB = new MyThreadB(myService213);
            myThreadB.start();
        }
    }
    
    
    

    输出:

    生产者在生产...
    消费者在消费
    生产者在生产...
    消费者在消费
    生产者在生产...
    消费者在消费
    生产者在生产...
    消费者在消费
    

    4.2 缓存支持多个值

    package lock6_consumer;
    
    import com.sun.org.apache.xml.internal.resolver.readers.ExtendedXMLCatalogReader;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * Created by lenovo on 2018/4/23.
     */
    public class MyService215 {
        private ReentrantLock lock = new ReentrantLock();
        private Condition condition = lock.newCondition();
    
        private int valueCount = 0;
        private int bufferMaxSize = 20; 
    
        /**
         * 生产者
         */
        public void set() {
            try {
                lock.lock();
                while (valueCount == bufferMaxSize) {
                    // 如果缓存已满,释放锁等待
                    condition.await();
                }
                // 唤醒等待线程
                condition.signalAll();
    
                // 缓存未满
                valueCount++; // 生产一个 添加到缓存中
                System.out.println("生产者生产,缓存中的数据共" + valueCount + "个");
    
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
        /**
         * 消费者
         */
    
        public void get() {
            try {
                lock.lock();
                while (valueCount == 0) {
                    condition.await();
                }
                condition.signalAll();
                // 消费
                valueCount--;
                System.out.println("消费者消费,缓存中的数据剩余:" + valueCount + " 个");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
    }
    
    /**
     * Created by lenovo on 2018/4/23.
     */
    public class MyThreadA extends Thread {
        private MyService215 myService213;
    
        public MyThreadA(MyService215 myService213) {
            this.myService213 = myService213;
        }
    
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                myService213.get();
            }
        }
    }
    public class MyThreadB extends Thread {
        private MyService215 myService213;
    
        public MyThreadB(MyService215 myService213){
            this.myService213 = myService213;
        }
    
        @Override
        public void run() {
            for(int i=0;i<100;i++){
                myService213.set();
            }
        }
    }
    public class Run215 {
        public static void main(String[] args) {
            MyService215 myService213 = new MyService215();
    
    
            MyThreadB consumer = new MyThreadB(myService213);
            consumer.start();
    
            MyThreadA producer = new MyThreadA(myService213);
            producer.start();
    
        }
    }
    
    

    输出:

    生产者生产,缓存中的数据共1个
    生产者生产,缓存中的数据共2个
    生产者生产,缓存中的数据共3个
    生产者生产,缓存中的数据共4个
    消费者消费,缓存中的数据剩余:3 个
    消费者消费,缓存中的数据剩余:2 个
    消费者消费,缓存中的数据剩余:1 个
    消费者消费,缓存中的数据剩余:0 个
    生产者生产,缓存中的数据共1个
    生产者生产,缓存中的数据共2个
    生产者生产,缓存中的数据共3个
    生产者生产,缓存中的数据共4个
    生产者生产,缓存中的数据共5个
    ....
    

    4.3 使用不同的 Condition 唤醒同类线程

    在 4.2 中使用 condition.signalAll() 是为了唤醒所有处于WAITING的线程,避免出现假死现象,但如果唤醒的是同类线程, 则又会进入WAITING状态,浪费资源

    解决方案: 一类线程使用一个 Condition对象

    package lock5_producer;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * Created by lenovo on 2018/5/11.
     */
    public class Service {
        private ReentrantLock lock = new ReentrantLock();
        private Condition producerCondition = lock.newCondition();
        private Condition consumerCondition = lock.newCondition();
    
        private int bufferMaxSize = 2;
        private int nowCount = 0;
    
        public void produce() {
            try {
                lock.lock();
                while (nowCount == bufferMaxSize) {
                    System.out.println("生产者: "+Thread.currentThread().getName()+" 等待");
                    producerCondition.await();
                    System.out.println("生产者: "+Thread.currentThread().getName()+" 被唤醒");
                }
                nowCount++;
                consumerCondition.signal();
                System.out.println("生产者: "+Thread.currentThread().getName()+"生产产品,总计" + nowCount);
    
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
        public void consumer() {
            try {
                lock.lock();
                while (nowCount == 0) {
                    System.out.println("消费者: "+Thread.currentThread().getName()+"等待");
                    consumerCondition.await();
                    System.out.println("消费者: "+Thread.currentThread().getName()+"被唤醒");
                }
                nowCount--;
                System.out.println("消费者: "+Thread.currentThread().getName()+" 消费产品,剩余 " + nowCount);
                producerCondition.signalAll();
            } catch (Exception e) {
                e.printStackTrace();
            }   finally {
                lock.unlock();
            }
        }
    }
    package lock5_producer;
    
    /**
     * Created by lenovo on 2018/5/11.
     */
    public class Main {
        public static void main(String[] args) {
            Service service = new Service();
            Runnable proRun = new Runnable() {
                @Override
                public void run() {
                    service.produce();
                }
            };
            Runnable conRun = new Runnable() {
                @Override
                public void run() {
                    service.consumer();
                }
            };
    
            Thread[] consumer = new Thread[30];
            Thread[] producer = new Thread[30];
            for (int i = 0; i < 30; i++) {
                consumer[i] = new Thread(conRun);
                consumer[i].setName("consumer" + i);
                producer[i] = new Thread(proRun);
                producer[i].setName("producer" + i);
            }
            for (int i = 0; i < 30; i++) {
                consumer[i].start();
                producer[i].start();
            }
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
        }
    }
    
    
    
    

    输出

    生产者: producer25 等待
    生产者: producer2 等待
    消费者: consumer23被唤醒
    消费者: consumer23 消费产品,剩余 1
    消费者: consumer29 消费产品,剩余 0
    消费者: consumer28等待
    生产者: producer27生产产品,总计1
    消费者: consumer27 消费产品,剩余 0
    消费者: consumer25等待
    生产者: producer24生产产品,总计1
    消费者: consumer24 消费产品,剩余 0
    生产者: producer25 被唤醒
    生产者: producer25生产产品,总计1
    生产者: producer2 被唤醒
    生产者: producer2生产产品,总计2
    
    

    相关文章

      网友评论

          本文标题:可重入锁 ReentranLock 和 Condition

          本文链接:https://www.haomeiwen.com/subject/lksxdftx.html