• java并发控制:ReentrantLock Condition使用详解


    我们通过一个实际的例子来解释Condition的用法:

    我们要打印1到9这9个数字,由A线程先打印1,2,3,然后由B线程打印4,5,6,然后再由A线程打印7,8,9. 这道题有很多种解法,现在我们使用Condition来做这道题(使用Object的wait,notify方法的解法在这里)。

     
    package cn.outofmemory.locks;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class App {
        static class NumberWrapper {
            public int value = 1;
        }
    
        public static void main(String[] args) {
            //初始化可重入锁
            final Lock lock = new ReentrantLock();
            
            //第一个条件当屏幕上输出到3
            final Condition reachThreeCondition = lock.newCondition();
            //第二个条件当屏幕上输出到6
            final Condition reachSixCondition = lock.newCondition();
            
            //NumberWrapper只是为了封装一个数字,一边可以将数字对象共享,并可以设置为final
            //注意这里不要用Integer, Integer 是不可变对象
            final NumberWrapper num = new NumberWrapper();
            //初始化A线程
            Thread threadA = new Thread(new Runnable() {
                @Override
                public void run() {
                    //需要先获得锁
                    lock.lock();
                    try {
                        System.out.println("threadA start write");
                        //A线程先输出前3个数
                        while (num.value <= 3) {
                            System.out.println(num.value);
                            num.value++;
                        }
                        //输出到3时要signal,告诉B线程可以开始了
                        reachThreeCondition.signal();
                    } finally {
                        lock.unlock();
                    }
                    lock.lock();
                    try {
                        //等待输出6的条件
                        reachSixCondition.await();
                        System.out.println("threadA start write");
                        //输出剩余数字
                        while (num.value <= 9) {
                            System.out.println(num.value);
                            num.value++;
                        }
    
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                    }
                }
    
            });
    
    
            Thread threadB = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        lock.lock();
                        
                        while (num.value <= 3) {
                            //等待3输出完毕的信号
                            reachThreeCondition.await();
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                    }
                    try {
                        lock.lock();
                        //已经收到信号,开始输出4,5,6
                        System.out.println("threadB start write");
                        while (num.value <= 6) {
                            System.out.println(num.value);
                            num.value++;
                        }
                        //4,5,6输出完毕,告诉A线程6输出完了
                        reachSixCondition.signal();
                    } finally {
                        lock.unlock();
                    }
                }
    
            });
    
    
            //启动两个线程
            threadB.start();
            threadA.start();
        }
    }

    上述代码中有完整的注释,请参考注释,理解Condition的用法。

    基本思路就是首先要A线程先写1,2,3,这时候B线程应该等待reachThredCondition信号,而当A线程写完3之后就通过signal告诉B线程“我写到3了,该你了”,这时候A线程要等嗲reachSixCondition信号,同时B线程得到通知,开始写4,5,6,写完4,5,6之后B线程通知A线程reachSixCondition条件成立了,这时候A线程就开始写剩下的7,8,9了。

    为了更好的理解Condition的用法,我们再看下java官方提供的例子:

    package locks;
    
    
    
    
    import java.util.Random;
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    
    
    
    public class AppOfficial {
    
        /**
         * BoundedBuffer 是一个定长100的集合,当集合中没有元素时,take方法需要等待,直到有元素时才返回元素
         * 当其中的元素数达到最大值时,要等待直到元素被take之后才执行put的操作
         * @author yukaizhao
         *
         */
        static class BoundedBuffer {
            final Lock lock = new ReentrantLock();
            final Condition notFull = lock.newCondition();
            final Condition notEmpty = lock.newCondition();
    
            final Object[] items = new Object[100];
            int putptr, takeptr, count;
    
            public void put(Object x) throws InterruptedException {
                System .out.println("put wait lock");
                lock.lock();
                System.out.println("put get lock");
                try {
                    while (count == items.length) {
                        System.out.println("buffer full, please wait");
                        notFull.await();
                    }
                        
                    items[putptr] = x;
                    if (++putptr == items.length)
                        putptr = 0;
                    ++count;
                    notEmpty.signal();
                } finally {
                    lock.unlock();
                }
            }
    
    
    
    
            public Object take() throws InterruptedException {
                System.out.println("take wait lock");
                lock.lock();
                System.out.println("take get lock");
                try {
                    while (count == 0) {
                        System.out.println("no elements, please wait");
                        notEmpty.await();
                    }
                    Object x = items[takeptr];
                    if (++takeptr == items.length)
                        takeptr = 0;
                    --count;
                    notFull.signal();
                    return x;
                } finally {
                    lock.unlock();
                }
            }
        }
        
        public static void main(String[] args) {
            final BoundedBuffer boundedBuffer = new BoundedBuffer();
            
            Thread t1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("t1 run");
                    for (int i=0;i<1000;i++) {
                        try {
                            System.out.println("putting..");
                            boundedBuffer.put(Integer.valueOf(i));
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
                
            }) ;
            
            Thread t2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i=0;i<1000;i++) {
                        try {
                            Object val = boundedBuffer.take();
                            System.out.println(val);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
                
            }) ;
            
            t1.start();
            t2.start();
        }
    }

    这个示例中BoundedBuffer是一个固定长度的集合,这个在其put操作时,如果发现长度已经达到最大长度,那么会等待notFull信号,如果得到notFull信号会像集合中添加元素,并发出notEmpty的信号,而在其take方法中如果发现集合长度为空,那么会等待notEmpty的信号,同时如果拿到一个元素,那么会发出notFull的信号。

  • 相关阅读:
    orioledb pg 存储引擎
    nginx ngx_http_addition_module 模块openresty content_by_lua 不能生效的原因
    Windows下Erlang和RabbitMQ下载安装教程
    [建议收藏]缓存雪崩的处理办法
    【精选】Mysql BTree和B+Tree的结构?
    RabbitMQ用户和virtual hosts的添加以及授权
    程序员增加收入的几种方法
    基于redis的keys、scan删除ttl为1的key
    全面了解Nginx主要应用场景
    很佩服的一个Google佬,离职了。。
  • 原文地址:https://www.cnblogs.com/yaowen/p/6124629.html
Copyright © 2020-2023  润新知