《程序员修炼之道》-读书笔记七-线程安全的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();
    }
}

 

 

 

posted @ 2019-10-10 11:54  草莓罐头  阅读(182)  评论(0编辑  收藏  举报