一,Producer-Consumer模式

Producer:生产者的意思,指的是生成数据的线程。
Consumer:消费者的意思,指的是使用数据的线程
当生产者和消费者以不同的线程运行时,两者之间的处理速度差异就会引起问题。比如,消费者想获取数据,可是数据还没有生成。
或者生产者想要交付数据,而消费者的状态还无法接收数据。
Producer-Consumer模式在生产者消费者之间加入了一个桥梁角色。该桥梁角色用于消除线程间处理速度的差异。
在该模式中,生产者和消费者都有多个,当消费者和生产者都只有一个时,称之为Pipe模式

二,例子

/**
* Table类用于表示放置蛋糕的桌子.
* 存放蛋糕的容器是数组。所以放置蛋糕是有顺序的,拿取蛋糕也是有顺序的。
*/
public class Table {
private final String[] buffer; //实际存放蛋糕的容器
private int tail;//下次put的位置
private int head;//下次take的位置
private int count;//buffer中蛋糕的个数 public Table(int count) {
this.buffer = new String[count];
this.tail = 0;
this.head = 0;
this.count = 0;
}
//放置蛋糕
public synchronized void put(String cake)throws InterruptedException{
System.out.println(Thread.currentThread().getName()+" put "+cake);
while (count >= buffer.length){
wait();
}
buffer[tail] = cake;
tail = (tail+1)%buffer.length;
count++;
notifyAll();
} //拿取蛋糕
public synchronized String take()throws InterruptedException{
while (count <= 0){
wait();
}
String cake = buffer[head];
head = (head+1)%buffer.length;
count--;
notifyAll();
System.out.println(Thread.currentThread().getName()+" takes "+cake);
return cake;
}
}
public class MakerThread extends Thread {
private final Random random;
private final Table table;
private static int id =0;//蛋糕的流水号 public MakerThread(String name, Table table, long seed) {
super(name);
this.random = new Random(seed);
this.table = table;
} @Override
public void run() {
try {
while (true){
Thread.sleep(random.nextInt(1000));
String cake = "[ Cake NO."+nextId() +" by "+getName()+"]";
table.put(cake);
}
}catch (InterruptedException e){ }
}
private static synchronized int nextId(){
return id++;
}
}
public class EaterThread extends Thread {
private final Random random;
private final Table table; public EaterThread(String name, Table table, long seed) {
super(name);
this.random = new Random(seed);
this.table = table;
} @Override
public void run() {
try {
while (true){
String cake = table.take();
Thread.sleep(random.nextInt(1000));
}
}catch (InterruptedException e){
}
}
}
public class Test {
public static void main(String[] args) {
Table table = new Table(3);
new MakerThread("MakerThread-1",table,31415).start();
new MakerThread("MakerThread-2",table,92653).start();
new MakerThread("MakerThread-3",table,58979).start(); new EaterThread("EaterThread-1",table,32384).start();
new EaterThread("EaterThread-2",table,62643).start();
new EaterThread("EaterThread-3",table,38327).start(); }
}

三,InterruptedException异常

1.加了InterruptedException 的方法
java.lang.Object类的wait方法
java.lang.Thread类的sleep方法
java.lang.Thread类的的join方法
2.加了 throws InterruptedException 的方法可能会花费时间,但是可以取消
花费时间体现在:
执行wait方法,需要等待notify/notifyAll方法唤醒,需要花费时间
执行sleep方法,会暂停执行,这也花费时间
执行join方法,会等待指定线程终止,需要时间
可以取消体现在:
假如Alice线程执行了Thread.sleep(100000);我们可以在别的线程中使用 alice.interrupt();当执行了interrupt后,正在sleep的线程会终止暂停状态,alice线程抛出
InterruptedException异常。这样线程Alice的控制权就会转移到捕获该异常的catch语句块中。
3.interrupt方法只是改变了中断状态
上面调用interrupt后,线程抛出InterruptedException异常,只是因为alice线程调用了线程中断的方法(wait,sleep,join)。
当线程执行普通的逻辑处理时,即使别的线程调用alice.interrupt(); 也不会抛出异常,而是继续执行。只有当线程继续执行到sleep,wait,join等方法的调用时,
才会抛出InterruptedException异常
4,其他线程执行alice.interrupt()时,并不需要获取alice线程实例的锁,无论何时,任何线程都可以调用其他线程的interrupt方法
5.notify和interrupt的区别:
??????????

四,isInterrupted方法:检查中断状态

isInterrupted是Thread类的实例方法,用于检查指定线程的中断状态,
若指定线程处于中断状态:返回true,
未处于中断状态:返回false

五,Thread.Interrupted:检查并清除中断状态

该方法是Thread类的静态方法,用于检查并清除当前线程的中断状态(操作对象是线程本身)
若当前线程处于中断状态,返回true。并且当前线程的中断状态会被清楚。
若当前线程未处于中断状态,返回false。

六,java.util.concurrent包中的队列

1.BlockingQueue接口:阻塞队列
该接口表示在达到合适的状态之前线程一直阻塞(wait)的队列。实现类:
1).ArrayBlockingQueue类:基于数组的 BlockingQueue
元素个数有限的BlockingQueue
2).LinkedBlockingQueue类:基于链表的BlockingQueue
元素个数没有最大限制的,只要有内存,就可以一直put数据
3).PriorityBlockingQueue类:带有优先级的BlockingQueue
数据的优先级是根据Comparable接口的自然排序,或构造函数的Comparator接口决定的顺序指定的
4).SynchronousQueue类:直接传递的BlockingQueue
该类用于执行由Producer角色到Consumer角色的 直接传递。如果Producer角色先put,在Consumer角色take之前,Producer角色的线程将一直阻塞。
反之一样阻塞
2.ConcurrentLinkedQueue类:元素个数没有最大限制的线程安全队列
ConcurrentLinkedQueue中,内部数据结构是分开的,线程之间互不影响,所以就不需要进行互斥处理
3.使用ArrayBlockingQueue替代上面实例程序中的Table类
代码:

/**

 * 使用阻塞队列完成table的功能
*/
public class BlockQueueTable extends ArrayBlockingQueue<String>{
public BlockQueueTable(int capacity) {
super(capacity);
}
public void put(String cake) throws InterruptedException {
System.out.println(Thread.currentThread().getName()+" puts "+cake);
super.put(cake);
} public String take() throws InterruptedException {
String cake = super.take();
System.out.println(Thread.currentThread().getName()+" takes "+cake);
return cake;
}
}

七,java.util.concurrent.Exchanger类

该类用来交换两个线程的数据,只有当两个线程都准备好了,才会进行交换。不然其中一个线程会一直等待另一个线程

public class ExchangerTest  {
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
final Exchanger exchanger = new Exchanger();
service.execute(new Runnable() {
@Override
public void run() {
try {
String data1 = "aaa";
System.out.println("线程"+Thread.currentThread().getName()+
"正在准备把 "+data1+"换出去");
Thread.sleep(new Random().nextInt(3000));
String data2 = (String) exchanger.exchange(data1);
System.out.println("线程"+Thread.currentThread().getName()+
"换回的数据为"+data2);
}catch (InterruptedException e){
}
}
}); service.execute(new Runnable() {
@Override
public void run() {
try {
String data1 = "bbb";
System.out.println("线程"+Thread.currentThread().getName()+
"正在准备把 "+data1+"换出去"); Thread.sleep(new Random().nextInt(3000));
String data2 = (String) exchanger.exchange(data1);
System.out.println("线程"+Thread.currentThread().getName()+
"换回的数据为"+data2);
}catch (InterruptedException e){
}
}
}); }
}

运行结果:

线程pool-1-thread-1正在准备把 aaa换出去
线程pool-1-thread-2正在准备把 bbb换出去
线程pool-1-thread-2换回的数据为aaa
线程pool-1-thread-1换回的数据为bbb

多线程系列之六:Producer-Consumer模式的更多相关文章

  1. 多线程系列之三:Immutable 模式

    一,什么是Immutable模式?immutable就是不变的,不发生改变的.Immutable模式中存在着确保实例状态不发生变化改变的类.这些实例不需要互斥处理.String就是一个Immutabl ...

  2. 多线程系列之八:Thread-Per-Message模式

    一,Thread-Per-Message模式 翻译过来就是 每个消息一个线程.message可以理解为命令,请求.为每一个请求新分配一个线程,由这个线程来执行处理.Thread-Per-Message ...

  3. 多线程系列之五:Balking 模式

    一,什么是Balking模式 如果现在不合适执行这个操作,或者没必要执行这个操作,就停止处理,直接返回.在Balking模式中,如果守护条件不成立,就立即中断处理. 二,例子: 定期将当前数据内容写入 ...

  4. java多线程系列13 设计模式 Future 模式

    Future 模式 类似于ajax请求  页面异步的进行后台请求 用户无需等待请求的结果 就可以继续浏览或者操作 核心就是:去除了主函数的等待时间,并使得原本需要等待的时间段可以用于处理其他业务逻辑 ...

  5. java多线程系列 目录

    Java多线程系列1 线程创建以及状态切换    Java多线程系列2 线程常见方法介绍    Java多线程系列3 synchronized 关键词    Java多线程系列4 线程交互(wait和 ...

  6. 6. oracle学习入门系列之六 模式

    oracle学习入门系列之六 模式 上篇咱们学习记录了ORACLE数据库中的数据库结构.内存结构和进程等.篇幅 蛤蟆感觉偏多了.这次要休整下,每次笔记不宜太多,不然与书籍有何差别. 我们要保证的是每次 ...

  7. C# Producer Consumer (生产者消费者模式)demo

    第一套代码将producer Consumer的逻辑写到from类里了,方便在demo的显示界面动态显示模拟生产和消费的过程.     第二套代码将producer Consumer的逻辑单独写到一个 ...

  8. Java多线程系列--“JUC集合”07之 ArrayBlockingQueue

    概要 本章对Java.util.concurrent包中的ArrayBlockingQueue类进行详细的介绍.内容包括:ArrayBlockingQueue介绍ArrayBlockingQueue原 ...

  9. Java多线程系列--AQS之 LockSupport

    concurrent包是基于AQS (AbstractQueuedSynchronizer)框架的,AQS(JAVA CAS原理.unsafe.AQS)框架借助于两个类: Unsafe(提供CAS操作 ...

随机推荐

  1. LeetCode算法题-Sum of Left Leaves(Java实现)

    这是悦乐书的第217次更新,第230篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第85题(顺位题号是404).找到给定二叉树中所有左叶的总和.例如: 二叉树中有两个左叶 ...

  2. puppet使用 apache passsenger 作为前端 (debian)

    目录 1. 概要 2. nginx + passenger 配置 2.1. package 安装 2.2. 配置文件设置 2.3. 测试配置结果 2.4. 参考网址 概要 之前做过 apache + ...

  3. Markdown编辑器开发记录(二):Markdown编辑器的使用与开发入门

    Markdown编辑器的使用与开发入门 在部门做技术分享的时候简单整理了一下手里的资料 1 是什么 1.1 Markdown是一种轻量级标记语言 Markdown是一种轻量级标记语言,创始人为约翰·格 ...

  4. 简单理解Vue中的nextTick

    Vue中的nextTick涉及到Vue中DOM的异步更新,感觉很有意思,特意了解了一下.其中关于nextTick的源码涉及到不少知识,很多不太理解,暂且根据自己的一些感悟介绍下nextTick. 一. ...

  5. (转)Spring Boot 2 (八):Spring Boot 集成 Memcached

    http://www.ityouknow.com/springboot/2018/09/01/spring-boot-memcached.html Memcached 介绍 Memcached 是一个 ...

  6. UVA1025-A Spy in the Metro(动态规划)

    Problem UVA1025-A Spy in the Metro Accept: 713  Submit: 6160Time Limit: 3000 mSec Problem Descriptio ...

  7. 转://利用从awr中查找好的执行计划来优化SQL

    原文地址:http://blog.csdn.net/zengxuewen2045/article/details/53495613 同事反应系统慢,看下是不是有锁了,登入数据库检查,没有异常锁定,但发 ...

  8. ssm框架的整合搭建(三)

    mybatis逆向工程工具类的使用---mybatis  generator 项目结构 配置文件 <?xml version="1.0" encoding="UTF ...

  9. linux运行级别和开机流程

    linux有七个运行级别 运行级别0:系统停机状态,系统默认运行级别不能设为0,否则不能正常启动 运行级别1:单用户工作状态,root权限,用于系统维护,禁止远程登陆 运行级别2:多用户状态(没有NF ...

  10. i春秋-百度杯十月场-vld

    查看源码,有提示,index.php.txt  ,  进入得到文本. 不太看得懂,后来百度,大致就是,flag1=.......&flag2=......&flag3=...... , ...