《程序员修炼之道》-读书笔记七-线程安全的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; } }
冰激凌店类
/** * 冰激凌店以及测试方法 */ 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(); } }