Java多线程之通信
在并发编程中,我们需要处理两个关键问题:线程之间如何通信及线程之间如何同步。通信是指线程之间以何种机制来交换信息。在命令式编程中,线程之间的通信机制有两种:共享内存和消息传递。
java内存模型
线程之间的共享变量存储在主内存(main memory)中,每个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程以读/写共享变量的副本。
从上图来看,线程A与线程B之间如要通信的话,必须要经历下面2个步骤:
- 首先,线程A把本地内存A中更新过的共享变量刷新到主内存中去。
- 然后,线程B到主内存中去读取线程A之前已更新过的共享变量。
另一种消息传递的方式
生产者、消费者模式
- 生产者:没有生产之前通知消费者等待,生产产品结束之后,马上通知消费者消费
- 消费者:没有消费之前通知生产者等待,消费产品结束之后,通知生产者继续生产产品以供消费
其中使用了缓冲区,需要考虑下面两点
- 从缓冲区取出产品和向缓冲区投放产品必须是互斥进行的。可以用关键段和互斥量来完成。
- 生产者要等待缓冲区为空,这样才可以投放产品,消费者要等待缓冲区不为空,这样才可以取出产品进行消费。并且由于有二个等待过程,所以要用二个事件或信号量来控制。
两种发放实现生产者、消费者模式
看看下面例子:
生产者: 往一个公共的盒子里面放苹果
消费者:从公共的盒子里面取苹果
盒子:盒子的容量不能超过5
方法一:wait() 和 notify() 通信方法实现
public class PublicBox { private int apple = 0; public synchronized void increace() { while (apple ==5) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } apple++; System. out .println("生成苹果成功!" ); notify(); } public synchronized void decreace() { while (apple ==0) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } apple--; System. out.println( "消费苹果成功!" ); notify(); } public static void main(String []args) { PublicBox box= new PublicBox(); Consumer con= new Consumer(box); Producer pro= new Producer(box); Thread t1= new Thread(con); Thread t2= new Thread(pro); t1.start(); t2.start(); } }
多个生产者和多个消费者
生产者代码(定义十次): public class Producer implements Runnable { private PublicBox box; public Producer(PublicBox box) { this .box = box; } @Override public void run() { for( int i=0;i<10;i++) { try { System. out .println("pro i:" +i); Thread. sleep(30); } catch (InterruptedException e) { // TODO: handle exception e.printStackTrace(); } box.increace(); } } } 消费者代码(同样十次): public class Consumer implements Runnable { private PublicBox box; public Consumer(PublicBox box) { this .box = box; } @Override public void run() { for( int i=0;i<10;i++) { try { System. out .println("Con: i " +i); Thread. sleep(3000); // 这里设置跟上面30不同是为了 盒子中的苹果能够增加,不会生产一个马上被消费 } catch (InterruptedException e) { // TODO: handle exception e.printStackTrace(); } box.decreace(); } } }
结果
pro i:0
Con: i 0
生成苹果成功!
pro i:1
生成苹果成功!
pro i:2
生成苹果成功!
pro i:3
生成苹果成功!
pro i:4
生成苹果成功!
pro i:5
消费苹果成功!
Con: i 1
生成苹果成功!
pro i:6
消费苹果成功!
Con: i 2
生成苹果成功!
pro i:7
消费苹果成功!
生成苹果成功!
pro i:8
Con: i 3
消费苹果成功!
生成苹果成功!
pro i:9
Con: i 4
消费苹果成功!
生成苹果成功!
Con: i 5
消费苹果成功!
Con: i 6
消费苹果成功!
Con: i 7
消费苹果成功!
Con: i 8
消费苹果成功!
Con: i 9
消费苹果成功!
方法二:阻塞队列实现生产者消费者模式
阻塞队列实现生产者消费者模式超级简单,它提供开箱即用支持阻塞的方法put()和take(),开发者不需要写困惑的wait-nofity代码去实现通信。BlockingQueue 一个接口,Java5提供了不同的现实,如ArrayBlockingQueue和LinkedBlockingQueue,两者都是先进先出(FIFO)顺序。而ArrayLinkedQueue是自然有界的,LinkedBlockingQueue可选的边界。下面这是一个完整的生产者消费者代码例子,对比传统的wait、nofity代码,它更易于理解。
盒子代码
import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; public class PublicBoxQueue { public static void main(String []args) { BlockingQueue publicBoxQueue= new LinkedBlockingQueue(5); //定义了一个大小为5的盒子 Thread pro= new Thread(new ProducerQueue(publicBoxQueue)); Thread con= new Thread(new ConsumerQueue(publicBoxQueue)); pro.start(); con.start(); } }
生产者
package dsd; import java.util.concurrent.BlockingQueue; public class ProducerQueue implements Runnable { private final BlockingQueue proQueue; public ProducerQueue(BlockingQueue proQueue) { this .proQueue =proQueue; } @Override public void run() { // TODO Auto-generated method stub for (int i=0;i<10;i++) { try { System. out .println("生产者生产的苹果编号为 : " +i); //放入十个苹果编号 为1到10 proQueue .put(i); /*Thread.sleep(3000);*/ } catch (InterruptedException e) { // TODO: handle exception e.printStackTrace(); } } } }
消费者
package dsd; import java.util.concurrent.BlockingQueue; public class ConsumerQueue implements Runnable { private final BlockingQueue conQueue; public ConsumerQueue(BlockingQueue conQueue) { this .conQueue =conQueue; } @Override public void run() { // TODO Auto-generated method stub for (int i=0;i<10;i++) { try { System. out .println("消费者消费的苹果编号为 :" +conQueue .take()); Thread. sleep(3000); //在这里sleep是为了看的更加清楚些 } catch (InterruptedException e) { // TODO: handle exception e.printStackTrace(); } } } }
结果
生产者生产的苹果编号为 : 0
生产者生产的苹果编号为 : 1
消费者消费的苹果编号为 :0
生产者生产的苹果编号为 : 2
生产者生产的苹果编号为 : 3
生产者生产的苹果编号为 : 4
生产者生产的苹果编号为 : 5
生产者生产的苹果编号为 : 6
生产者生产的苹果编号为 : 7
消费者消费的苹果编号为 :1
消费者消费的苹果编号为 :2
生产者生产的苹果编号为 : 8
消费者消费的苹果编号为 :3
生产者生产的苹果编号为 : 9
消费者消费的苹果编号为 :4
消费者消费的苹果编号为 :5
消费者消费的苹果编号为 :6
消费者消费的苹果编号为 :7
消费者消费的苹果编号为 :8
消费者消费的苹果编号为 :9
生产者消费者模式优点
- 解耦:分开生产者和消费者的依赖,耦合度低。
- 支持并发:生产者和消费者是两个独立的并发体,他们通过缓冲区作为桥梁,生产者只需要往缓冲区里丢数据,就可以生产下一个数据,消费者只需要在缓冲区中取数据,这样两个并发体都不会因为出来速度而引起阻塞。
- 支持忙闲不均:如果生产者时快时慢,缓冲区的好处就好大,当生产者速度很快,任务就会在缓冲区缓存起来,让消费者依次去执行;当生产者速度比较慢,消费者可以从缓冲区中取任务,消费者不需要等到生产了任务才有工作,除非缓冲区为空。
应用场景
- 订单系统
- 一些需要缓存的任务系统
相关推荐
一张图方便理解和掌握java 多线程之间通信的实质 java 多线程 其实就是每个线程都拥有自己的内存空间,多线程之间的通信,比例A线程修改了主内存(main方法的线程)变量,需要把A线程修改的结果同步到主线程中,...
java tcp socket 多线程多句通信
java多线程经典案例,线程同步、线程通信、线程阻塞等经典案例
结合大量实例,全面讲解Java多线程编程中的并发访问、线程间通信、锁等最难突破的核心技术与应用实践 Java多线程无处不在,如服务器、数据库、应用。多线程可以有效提升计算和处理效率,大大提升吞吐量和可伸缩性,...
Java的多线程-线程间的通信.doc
Java 线程通信示例 源代码 多线程之间通信,值得收藏
JAVA写的多线程socket通信程序源码.我写的作业。
java多线程实现TCP连接UDP聊天的聊天程序
Java多线程技术在网络通信系统中的应用.pdf
学习线程之间的通信协调关系。 2. 使用 runnable 接口实现按两个不同的时间间隔( 1 秒和 3 秒)在屏幕上显示当前时间。 3.写一个程序,模拟4个售票窗口共同卖100张火车票的程序。 使用继承Thread类方式和实现...
基于多线程实现的JavaSocket客户端-服务端点对点异步通信程序代码
java多线程知识点,源代码案例,代码案例中包括如何创建线程,主线程,线程优先级,线程组,线程同步,死锁,线程间的通信知识点
本程序利用了Java多线程进行了TCP的端口扫描,能够满足一般的业务需求,适用与网管或网络安全从事者
Java多线程--线程间的通信
Java多线程两个例子,一个是自定义线程池方法实现多线程运行个数的控制,可以解决多线程内存不足的问题。另一个是利用线程通信,读写先关的典型实例
关于java多线程技术的实验 掌握java语言中的多线程编程技巧 掌握Thread类和Runnable接口 掌握java语言中多线程的生命周期 掌握java语言中线程的通信技巧
资源名称:Java多线程与并发库高级应用视频教程22集资源目录:【】01传统线程技术回顾【】02传统定时器技术回顾【】03传统线程互斥技术【】04传统线程同步通信技术【】04传统线程同步通信技术_分割纪录【】05线程...
这是利用java的udp通信,利用多线程保持通信持续的java聊天小程序
了解多一点,对你的Java之路成长一定有帮助! 这个PDF,我在网上找了好久,好东西来的~~~~
网络通信之Java Socket多线程通信.pdf