• 面试题:Java并发编程生产者和消费者有序消费问题


    摘要:对于一个资源对象,必须先生产再消费,消费后再生产,如此循环往复。为了解决这个并发问题,提供两种解决方案,一是使用synchronized关键字和Object对象的监听器,二是使用java.util.concurrent.locks下的类Lock和Condition。

    §前言

      定义一个实体类,名为资源,它有两个属性,分别是姓名和性别。兹有两个线程, 一个是生产者,负责给资源实例的姓名和性别赋值,一个是消费者,负责打印资源实例的姓名和性别的值。

      要求:有序生产和消费资源,即对于一个资源对象,必须先生产-消费-生产-消费,如此循环往复;当然了,第一个操作可能是消费者,这是没有资源可以消费的。

      解决问题:生产者对资源对象赋值后进入等待状态,并唤醒消费者输出资源属性,输出后立刻唤醒生产者。一般要加入个标志进行辅助,判断能否生产或者消费。

    §案例分析

      有两个解决方案,一是使用synchronized关键字,二是使用java.util.concurrent.locks下的类Lock和Condition。两个方案都需要加入一个标志,标记当前是否允许打印或者赋值。

    §使用同步锁synchronized关键字

    package com.eg.wiener.conccurrency;
    
    /**
     * 妖的出现,有序打印字符串信息
     */
    class Resource {
        /* 姓名 */
        private String name;
        /* 性别 */
        private String sex;
        //标记一个操作单元是否完成,true 是
        private boolean flag = false;
    
        //赋值功能。
        public synchronized void set(String name, String sex) {
            if (flag) {
                try {
                    //  赋值函数进入等待状态
                    this.wait();
                } catch (InterruptedException e) {
                }
            }
            this.name = name;
            this.sex = sex;
            flag = true;
            // 赋值结束,唤醒打印函数
            this.notify();
        }
    
        /**
         * 消费者,获取资源属性
         */
        public synchronized void out() {
            if (!flag)
                try {
                    //进入等待状态,停止打印
                    this.wait();
                } catch (InterruptedException e) {
                }
            System.out.println(name + "------" + sex);
            flag = false;
            //打印结束,唤醒资源赋值函数
            this.notify();
        }
    }
    
    //生产者,赋值线程任务
    class Input implements Runnable {
        private Resource r;
        //任务一初始化就必须有要处理的资源。
        Input(Resource r) {
            this.r = r;
        }
        // 赋值
        public void run() {
            int x = 0;
            while (true) {
                if (x == 0) {
                    r.set("张飞", "男");
                } else {
                    r.set("rose", "女女女女");
                }
                //根据取模结果实现切换
                x = (x + 1) % 2;
            }
        }
    }
    
    //获取值线程任务
    class Output implements Runnable {
        private Resource r;
    
        Output(Resource r) {
            this.r = r;
        }
    
        public void run() {
            while (true) {
                r.out();
            }
        }
    }
    
    public class ThreadTest2_3 {
        public static void main(String[] args) {
            Resource r = new Resource();
            Input in = new Input(r);
            Output out = new Output(r);
            Thread t1 = new Thread(in);
            Thread t2 = new Thread(out);
            t1.start();
            t2.start();
        }
    }
    

      实现方案分析:通过synchronized关键字锁定同一个资源(记住 currentRes)后,定义标记flag,由flag的值决定打印或者赋值操作是否允许,不允许的时候就调用currentRes的wait函数,让出CPU执行权,唤醒另一个操作。

    §使用JUC的Lock和Condition

      在 Condition 对象中,await、signal 和 signalAll三个方法分别对应于 Object实例的三个监视器方法 wait、notify 和 notifyAll。Condition 接口与 Lock 配合实现了等待/通知模式,要为某个 Lock 对象(例如 aLock)获得 Condition 对象,需要aLock调用函数newCondition() 。

    package com.eg.wiener.conccurrency;
    
    import java.util.concurrent.locks.*;
    
    /**
     * 程序改成JDK1.5的Lock Condition接口
     */
    class Resource {
        private String name;
        private String sex;
        //定义标记
        private boolean flag = false;
    
        //先创建锁对象
        private final Lock lock = new ReentrantLock();
    
        //通过锁对象获取监视器对象
        private Condition con = lock.newCondition();
    
        //赋值功能
        public void set(String name, String sex) {
            lock.lock();
            try {
                if (flag) {
                    try {
                        //     该线程将释放锁、节点加入等待队列进入等待状态
                        con.await();
                    } catch (InterruptedException e) {
                    }
                }
                this.name = name;
                this.sex = sex;
                flag = true;
                con.signal();
            } finally {
                lock.unlock();
            }
        }
    
        //获取值
        public void out() {
            lock.lock();
            try {
                if (!flag) {
                    try {
                        con.await();
                    } catch (InterruptedException e) {
                    }
                }
                System.out.println(name + "------" + sex);
                flag = false;
                con.signal();
            } finally {
                lock.unlock();
            }
        }
    }
    
    //赋值线程任务
    class Input implements Runnable {
        private Resource r;
    
        //任务一初始化就必须有要处理的资源
        Input(Resource r) {
            this.r = r;
        }
    
        public void run() {
            int x = 0;
            while (true) {
                if (x == 0) {
                    r.set("张飞", "男");
                } else {
                    r.set("rose", "女女女女");
                }
                x = (x + 1) % 2;
            }
        }
    }
    
    //获取值线程任务
    class Output implements Runnable {
        private Resource r;
    
        Output(Resource r) {
            this.r = r;
        }
    
        public void run() {
            while (true) {
                r.out();
            }
        }
    }
    
    public class ThreadTest2_4 {
        public static void main(String[] args) {
            Resource r = new Resource();
            Input in = new Input(r);
            Output out = new Output(r);
            Thread t1 = new Thread(in);
            Thread t2 = new Thread(out);
            t1.start();
            t2.start();
        }
    }
    

    §小结

      文章到这里就结束了,看完之后你有什么想法想要跟大家分享呢?评论区在等着你!

    §Reference


      读后有收获,小礼物走一走,请作者喝咖啡。

    赞赏支持

  • 相关阅读:
    javascript模板方法模式
    设计模式之原型模式
    es6 工厂模式
    js建筑者模式
    程序运行时的堆栈与数据结构中的堆栈有何分别
    POJO、JavaBean、DTO的区别
    AnnotationTransactionAttributeSource is only available on Java 1.5 and higher
    进程pid理解
    Tasklist使用详解
    day 16 类的成员
  • 原文地址:https://www.cnblogs.com/east7/p/14402390.html
Copyright © 2020-2023  润新知