• 《程序员修炼之道》-读书笔记七-线程安全的Queue


    Java中有些多线程编程模式在很大程度上都依赖于Queue实现的线程安全性,所以很有必要充分认识它。

    队列经常用来在线程之间传递工作单元,这个模式通常适合用Queue最简单的并发扩展BlockingQueue来实现。

    1.BlockingQueue

    BlockingQueue还有两个特性。

    • 在向队列中put()时,如果队列己满,它会让放人线程等待队列腾出空间。
    • 在从队列中take()时,如果队列为空,会导致取出线程阻塞。

    BlockingQueue定义的常用方法如下:

      抛出异常 特殊值 阻塞  超时
    插入  add(e) offer(e)  put(e)  offer(e, time, unit)
    移除  remove() poll() take()  poll(time, unit)
    检查  element() peek() 不可用 不可用

     

    2.一个冰激凌店的小例子

    顾客类

    /**
     * 顾客类
     */
    public class Customer {
    
        private String name;
    
        public Customer(String name) {
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    View Code

    冰激凌店类  

    /**
     * 冰激凌店以及测试方法
     */
    public class IceCreamShop extends Thread{
    
        /**
         * 等待购买冰激凌的队伍
         */
        protected final BlockingQueue<Customer> queue;
    
        /**
         * 制作冰激凌的时间
         */
        protected final int restTime;
    
        /**
         * 故障标记
         */
        private boolean shutdown = false;
    
        public IceCreamShop(BlockingQueue queue, int restTime) {
            this.queue = queue;
            this.restTime = restTime;
        }
    
        @Override
        public void run() {
            while (!shutdown) {
                try {
                    Thread.sleep(restTime);
                    Customer customer = queue.take();
                    System.out.println(customer.getName() + "已取餐-------------");
                } catch (InterruptedException e) {
                    shutdown = true;
                }
            }
        }
    
        public static void main(String[] args) {
            // 设置队列中有三个顾客
            BlockingQueue<Customer> customers = new LinkedBlockingQueue<>();
            Customer customer1 = new Customer("zhangsan");
            Customer customer2 = new Customer("lisi");
            Customer customer3 = new Customer("ww");
            customers.add(customer1);
            customers.add(customer2);
            customers.add(customer3);
    
            IceCreamShop iceCreamShop = new IceCreamShop(customers, 1000);
            iceCreamShop.start();
        }
    }

    在IceCreamShop类的run()方法中,顾客需要先等待冰激凌店员制作冰激凌,然后再取餐,当没有顾客后,店员会一直等待顾客,进入阻塞状态。

    3. TransferQueue — Java7中的新贵

    Java7引人了TransferQueue。它本质上是多了一项transfer()操作的BlockingQueue。
    LinkedTransferQueue采用的一种预占模式。意思就是消费者线程取元素时,如果队列为空,那就生成一个节点(节点元素为null)入队,然后消费者线程被等待在这个节点上,后面生产者线程入队时发现有一个元素为null的节点,生产者线程就不入队了,直接就将元素填充到该节点,唤醒该节点等待的线程,被唤醒的消费者线程取走元素,从调用的方法返回。即找到匹配的节点不入队,找不到根据how参数入队。(简单说就是 进一个,出一个)

    冰激凌店类

    import java.util.concurrent.TransferQueue;
    import java.util.concurrent.atomic.AtomicInteger;
    
    public abstract class IceCreamShop extends Thread{
    
        /**
         * 等待购买冰激凌的队伍
         */
        protected final TransferQueue<Customer> queue;
    
        /**
         * 制作冰激凌的时间
         */
        protected final int restTime;
    
        /**
         * 顾客号
         */
        protected static AtomicInteger number = new AtomicInteger(1);
    
        /**
         * 故障标记
         */
        private boolean shutdown = false;
    
        public IceCreamShop(TransferQueue<Customer> queue, int restTime) {
            this.queue = queue;
            this.restTime = restTime;
        }
    
        @Override
        public void run() {
            while (!shutdown) {
                try {
                    Thread.sleep(restTime);
                } catch (InterruptedException e) {
                    shutdown = true;
                }
                doAction();
            }
        }
    
        public abstract void doAction();
    }

    测试方法类

    import java.util.concurrent.LinkedTransferQueue;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.TransferQueue;
    
    public class IceCreamShopMain {
        public static void main(String[] args) {
    
            final TransferQueue<Customer> customers = new LinkedTransferQueue<Customer>();
    
            /**
             * 店员制作冰激凌
             */
            IceCreamShop t1 = new IceCreamShop(customers, 100) {
                @Override
                public void doAction() {
                    Customer customer = new Customer("顾客:" + number);
                    boolean handed = false;
                    try {
                        handed = customers.tryTransfer(customer, 100, TimeUnit.MILLISECONDS);
                    } catch (InterruptedException e) {
                    }
                    if (!handed) {
                        System.out.println("这个店员做的很快,趁着顾客取餐的时间,去吃了根薯条");
                    } else {
                        System.out.println(customer.getName() + "的冰激凌已制作完成---------请取餐");
                        number.getAndIncrement();
                    }
                }
            };
    
            /**
             * 顾客取冰激凌
             */
            IceCreamShop t2 = new IceCreamShop(customers, 100) {
                @Override
                public void doAction() {
                    Customer customer = null;
                    try {
                        customer = customers.take();
                        Thread.sleep(900);
                        System.out.println(customer.getName() + "已取餐----------------");
                    } catch (InterruptedException e) {
                        return;
                    }
                }
            };
    
            t1.start();
            t2.start();
        }
    }

     

  • 相关阅读:
    管理配置KVM,热添加、热迁移
    《google工作整理术》21条原则
    【教你玩转云计算】在阿里云一键安装快速部署Oracle11g 【转】
    Oracle数据库开启归档日志及rman备份情况查询
    【转】CentOS7一键部署OpenStack
    【转】基于openstack安装部署私有云详细图文教程
    Oracle Database 12c数据库中文配置安装图解教程(详细安装步骤)
    精通Linux(第2版) 第3章 设备管理
    精通Linux(第2版) 第2章 基础命令和目录结构
    四种IO 模型
  • 原文地址:https://www.cnblogs.com/Deters/p/11647095.html
Copyright © 2020-2023  润新知