JAVA多线程之线程间的通信方式
(转发) 收藏 记 周日,北京的天阳光明媚,9月,北京的秋格外肃穆透彻,望望窗外的湛蓝的天,心似透过栏杆,沐浴在这透亮清澈的蓝天里,那朵朵白云如同一朵棉絮,心意畅想....思绪外扬, 鱼和熊掌不可兼得,徜徉在知识的海洋里,收获满满...比起这时光,尽管有些可惜.还是
充满满足,或许在心里想想外边的美好,更美吧.......
一,介绍
本总结我对于JAVA多线程中线程之间的通信方式的理解,主要以代码结合文字的方式来讨论线程间的通信,故摘抄了书中的一些示例代码。
二,线程间的通信方式
①同步
这里讲的同步是指多个线程通过synchronized关键字这种方式来实现线程间的通信。
参考示例:

- public class MyObject {
- synchronized public void methodA() {
- //do something....
- }
- synchronized public void methodB() {
- //do some other thing
- }
- }
- public class ThreadA extends Thread {
- private MyObject object;
- //省略构造方法
- @Override
- public void run() {
- super.run();
- object.methodA();
- }
- }
- public class ThreadB extends Thread {
- private MyObject object;
- //省略构造方法
- @Override
- public void run() {
- super.run();
- object.methodB();
- }
- }
- public class Run {
- public static void main(String[] args) {
- MyObject object = new MyObject();
- //线程A与线程B 持有的是同一个对象:object
- ThreadA a = new ThreadA(object);
- ThreadB b = new ThreadB(object);
- a.start();
- b.start();
- }
- }

由于线程A和线程B持有同一个MyObject类的对象object,尽管这两个线程需要调用不同的方法,但是它们是同步执行的,比如:线程B需要等待线程A执行完了methodA()方法之后,它才能执行methodB()方法。这样,线程A和线程B就实现了 通信。
这种方式,本质上就是“共享内存”式的通信。多个线程需要访问同一个共享变量,谁拿到了锁(获得了访问权限),谁就可以执行。
②while轮询的方式
代码如下:

- 1 import java.util.ArrayList;
- 2 import java.util.List;
- 3
- 4 public class MyList {
- 5
- 6 private List<String> list = new ArrayList<String>();
- 7 public void add() {
- 8 list.add("elements");
- 9 }
- 10 public int size() {
- 11 return list.size();
- 12 }
- 13 }
- 14
- 15 import mylist.MyList;
- 16
- 17 public class ThreadA extends Thread {
- 18
- 19 private MyList list;
- 20
- 21 public ThreadA(MyList list) {
- 22 super();
- 23 this.list = list;
- 24 }
- 25
- 26 @Override
- 27 public void run() {
- 28 try {
- 29 for (int i = 0; i < 10; i++) {
- 30 list.add();
- 31 System.out.println("添加了" + (i + 1) + "个元素");
- 32 Thread.sleep(1000);
- 33 }
- 34 } catch (InterruptedException e) {
- 35 e.printStackTrace();
- 36 }
- 37 }
- 38 }
- 39
- 40 import mylist.MyList;
- 41
- 42 public class ThreadB extends Thread {
- 43
- 44 private MyList list;
- 45
- 46 public ThreadB(MyList list) {
- 47 super();
- 48 this.list = list;
- 49 }
- 50
- 51 @Override
- 52 public void run() {
- 53 try {
- 54 while (true) {
- 55 if (list.size() == 5) {
- 56 System.out.println("==5, 线程b准备退出了");
- 57 throw new InterruptedException();
- 58 }
- 59 }
- 60 } catch (InterruptedException e) {
- 61 e.printStackTrace();
- 62 }
- 63 }
- 64 }
- 65
- 66 import mylist.MyList;
- 67 import extthread.ThreadA;
- 68 import extthread.ThreadB;
- 69
- 70 public class Test {
- 71
- 72 public static void main(String[] args) {
- 73 MyList service = new MyList();
- 74
- 75 ThreadA a = new ThreadA(service);
- 76 a.setName("A");
- 77 a.start();
- 78
- 79 ThreadB b = new ThreadB(service);
- 80 b.setName("B");
- 81 b.start();
- 82 }
- 83 }

在这种方式下,线程A不断地改变条件,线程ThreadB不停地通过while语句检测这个条件(list.size()==5)是否成立 ,从而实现了线程间的通信。但是这种方式会浪费CPU资源。之所以说它浪费资源,是因为JVM调度器将CPU交给线程B执行时,它没做啥“有用”的工作,只是在不断地测试 某个条件是否成立。就类似于现实生活中,某个人一直看着手机屏幕是否有电话来了,而不是: 在干别的事情,当有电话来时,响铃通知TA电话来了。关于线程的轮询的影响,可参考:JAVA多线程之当一个线程在执行死循环时会影响另外一个线程吗?
这种方式还存在另外一个问题:
轮询的条件的可见性问题,关于内存可见性问题,可参考:JAVA多线程之volatile 与 synchronized 的比较中的第一点“一,volatile关键字的可见性”
线程都是先把变量读取到本地线程栈空间,然后再去再去修改的本地变量。因此,如果线程B每次都在取本地的 条件变量,那么尽管另外一个线程已经改变了轮询的条件,它也察觉不到,这样也会造成死循环。
③wait/notify机制
代码如下:

- 1 import java.util.ArrayList;
- 2 import java.util.List;
- 3
- 4 public class MyList {
- 5
- 6 private static List<String> list = new ArrayList<String>();
- 7
- 8 public static void add() {
- 9 list.add("anyString");
- 10 }
- 11
- 12 public static int size() {
- 13 return list.size();
- 14 }
- 15 }
- 16
- 17
- 18 public class ThreadA extends Thread {
- 19
- 20 private Object lock;
- 21
- 22 public ThreadA(Object lock) {
- 23 super();
- 24 this.lock = lock;
- 25 }
- 26
- 27 @Override
- 28 public void run() {
- 29 try {
- 30 synchronized (lock) {
- 31 if (MyList.size() != 5) {
- 32 System.out.println("wait begin "
- 33 + System.currentTimeMillis());
- 34 lock.wait();
- 35 System.out.println("wait end "
- 36 + System.currentTimeMillis());
- 37 }
- 38 }
- 39 } catch (InterruptedException e) {
- 40 e.printStackTrace();
- 41 }
- 42 }
- 43 }
- 44
- 45
- 46 public class ThreadB extends Thread {
- 47 private Object lock;
- 48
- 49 public ThreadB(Object lock) {
- 50 super();
- 51 this.lock = lock;
- 52 }
- 53
- 54 @Override
- 55 public void run() {
- 56 try {
- 57 synchronized (lock) {
- 58 for (int i = 0; i < 10; i++) {
- 59 MyList.add();
- 60 if (MyList.size() == 5) {
- 61 lock.notify();
- 62 System.out.println("已经发出了通知");
- 63 }
- 64 System.out.println("添加了" + (i + 1) + "个元素!");
- 65 Thread.sleep(1000);
- 66 }
- 67 }
- 68 } catch (InterruptedException e) {
- 69 e.printStackTrace();
- 70 }
- 71 }
- 72 }
- 73
- 74 public class Run {
- 75
- 76 public static void main(String[] args) {
- 77
- 78 try {
- 79 Object lock = new Object();
- 80
- 81 ThreadA a = new ThreadA(lock);
- 82 a.start();
- 83
- 84 Thread.sleep(50);
- 85
- 86 ThreadB b = new ThreadB(lock);
- 87 b.start();
- 88 } catch (InterruptedException e) {
- 89 e.printStackTrace();
- 90 }
- 91 }
- 92 }

线程A要等待某个条件满足时(list.size()==5),才执行操作。线程B则向list中添加元素,改变list 的size。
A,B之间如何通信的呢?也就是说,线程A如何知道 list.size() 已经为5了呢?
这里用到了Object类的 wait() 和 notify() 方法。
当条件未满足时(list.size() !=5),线程A调用wait() 放弃CPU,并进入阻塞状态。---不像②while轮询那样占用CPU
当条件满足时,线程B调用 notify()通知 线程A,所谓通知线程A,就是唤醒线程A,并让它进入可运行状态。
这种方式的一个好处就是CPU的利用率提高了。
但是也有一些缺点:比如,线程B先执行,一下子添加了5个元素并调用了notify()发送了通知,而此时线程A还执行;当线程A执行并调用wait()时,那它永远就不可能被唤醒了。因为,线程B已经发了通知了,以后不再发通知了。这说明:通知过早,会打乱程序的执行逻辑。
④管道通信就是使用java.io.PipedInputStream 和 java.io.PipedOutputStream进行通信
具体就不介绍了。分布式系统中说的两种通信机制:共享内存机制和消息通信机制。感觉前面的①中的synchronized关键字和②中的while轮询 “属于” 共享内存机制,由于是轮询的条件使用了volatile关键字修饰时,这就表示它们通过判断这个“共享的条件变量“是否改变了,来实现进程间的交流。
而管道通信,更像消息传递机制,也就是说:通过管道,将一个线程中的消息发送给另一个。
关于wait/notify更多内容,可参考:JAVA多线程之wait/notify
JAVA多线程之线程间的通信方式的更多相关文章
- java多线程与线程间通信
转自(http://blog.csdn.net/jerrying0203/article/details/45563947) 本文学习并总结java多线程与线程间通信的原理和方法,内容涉及java线程 ...
- Java多线程基础——线程间通信
在使用多线程的时候,经常需要多个线程进行协作来完成一件事情.在前面两章分析了Java多线程的基本使用以及利用synchronized来实现多个线程同步调用方法或者执行代码块.但上面两章的内容涉及到的例 ...
- Java多线程中线程间的通信
一.使用while方式来实现线程之间的通信 package com.ietree.multithread.sync; import java.util.ArrayList; import java.u ...
- Java多线程:线程间通信之volatile与sychronized
由前文Java内存模型我们熟悉了Java的内存工作模式和线程间的交互规范,本篇从应用层面讲解Java线程间通信. Java为线程间通信提供了三个相关的关键字volatile, synchronized ...
- Java多线程:线程间通信之Lock
Java 5 之后,Java在内置关键字sychronized的基础上又增加了一个新的处理锁的方式,Lock类. 由于在Java线程间通信:volatile与sychronized中,我们已经详细的了 ...
- Java——多线程之线程间通信
Java多线系列文章是Java多线程的详解介绍,对多线程还不熟悉的同学可以先去看一下我的这篇博客Java基础系列3:多线程超详细总结,这篇博客从宏观层面介绍了多线程的整体概况,接下来的几篇文章是对多线 ...
- java多线程:线程间通信——生产者消费者模型
一.背景 && 定义 多线程环境下,只要有并发问题,就要保证数据的安全性,一般指的是通过 synchronized 来进行同步. 另一个问题是,多个线程之间如何协作呢? 我们看一个仓库 ...
- Java多线程之线程的通信
Java多线程之线程的通信 在总结多线程通信前先介绍一个概念:锁池.线程因为未拿到锁标记而发生的阻塞不同于前面五个基本状态中的阻塞,称为锁池.每个对象都有自己的锁池的空间,用于放置等待运行的线程.这些 ...
- iOS开发多线程篇—线程间的通信
iOS开发多线程篇—线程间的通信 一.简单说明 线程间通信:在1个进程中,线程往往不是孤立存在的,多个线程之间需要经常进行通信 线程间通信的体现 1个线程传递数据给另1个线程 在1个线程中执行完特定任 ...
随机推荐
- day73 Django框架之URL
Django框架之url路由层一 Django数据库的一对多与多对多表的建立 一对多 publish_id的建立:publish=models.ForeignKey(to='Publish', to ...
- 配置samba的流程
1.关闭防火墙和selinuxservice iptables stopsetenforce 02.配置本地yummount /dev/cdrom /mediacd /etc/yum.repos.dc ...
- JavaScript数组方法--every、some、fill
接上一篇,JavaScript数组方法--concat.push,继续其他的数组方法. every:every() 方法测试数组的所有元素是否都通过了指定函数的测试. 先看其使用方法: functio ...
- 2018-2019-2 20165205 网络攻防Exp3免杀原理与实践
2018-2019-2 20165205 网络攻防Exp3免杀原理与实践 一.实践内容 1.1正确使用msf编码器,msfvenom生成如jar之类的其他文件,veil-evasion,加壳工具,使用 ...
- orcal - 约束
数据库约束 1.非空约束 DROP TABLE member PURGE; CREATE TABLE member( mid number , name varchar2(20) not null, ...
- ichartjs用法
代码 <script type="text/javascript" src="../js/ichart.1.2.min.js"></scrip ...
- idea中git常见使用场景
工作中多人使用版本控制软件协作开发,常见的应用场景归纳如下: 假设小组中有两个人,组长小张,组员小袁 场景一:小张创建项目并提交到远程Git仓库 场景二:小袁从远程Git仓库上获取项目源码 场景三:小 ...
- ---mipi command
可惜这是5.1系统: http://www.cnblogs.com/lialong1st/p/8534728.html
- Golang源码探索(一) 编译和调试源码(转)
GO可以说是近几年最热门的新兴语言之一了, 一般人看到分布式和大数据就会想到GO,这个系列的文章会通过研究golang的源代码来分析内部的实现原理,和CoreCLR不同的是, golang的源代码已经 ...
- JavaScript Array some() 方法
some 判断数组中是否至少有一个元素满足条件 只要有一个满足就返回true 只有都不满足时才返回false 语法: array.some(function(value,index,array),th ...