java的线程之间如何进行消息传递
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java的线程之间如何进行消息传递相关的知识,希望对你有一定的参考价值。
比如说我有Thread1 和Thread2 两个线程,Thread1 和Thread2之间可以互相send 和receive 消息,我可以自己定义一个message类,请问这种用java可以实现吗,代码怎么写啊
我说的是普通java程序,不是网页
我想实现的是这样的, 比如Thread1发个消息给Thread2, 内容是让Thread2干A事,Thread2接到这个消息,就开始干A事。这应该是异步的,如果通过共享内存怎么实现呢,如果自己实现,应该怎么实现? 谢谢啊
追答这可以有很多种实现方式,比如可以用CountDownLatch,初始时用await阻塞线程2,当Thread1做完事情之后打开这个latch(一般叫闭锁)。还可有用信号量,或者CycleBarrier之类的数据结构。
如果实现一套简单消息传递机制,可以抽象出一个Actor,附带一个mailbox(可以是一个BlockingQueue),它是一个Runnable,它的任务是不断扫描mailbox里的消息并处理。Thread2也可以抽象出一个Actor,它的任何很简单,做自己的事,并在完成后把消息放到Thread1的mailbox里。两个Runnable可以用JDK线程池自动调度
对不起我比较菜 没太明白,我这里面thread1 和thread2是同等地位的,它们都从事这自己的事情,但是当thread1发消息给thread2时,thread2收到消息就开始做thread1叫它做的事。如果用mailbox,你可以告诉我代码吗? 谢谢啦。
追答这里没必要实现复杂的消息机制,我写个简单的代码给你
public static void main(String[] args)
class Thread1 extends Thread
private BlockingQueue mailbox;
/**
*
*/
public Thread1(BlockingQueue mailbox)
this.mailbox = mailbox;
public void run()
try
Object message = mailbox.take();
System.out.println(message);
catch (InterruptedException e)
// TODO Auto-generated catch block
e.printStackTrace();
;
class Thread2 extends Thread
private BlockingQueue sendBuffer;
/**
*
*/
public Thread2(BlockingQueue sendBuffer)
this.sendBuffer = sendBuffer;
@Override
public void run()
try
System.out.println("sending message hello to another thread");
sendBuffer.put("hello");
catch (InterruptedException e)
// TODO Auto-generated catch block
e.printStackTrace();
BlockingQueue queue = new LinkedBlockingQueue();
Thread t1 = new Thread1(queue);
Thread t2 = new Thread2(queue);
t1.run();
t2.run();
原生Java线程之间只能通过共享内存(同一个虚拟机内)来通信,当然你可以通过自己实现,使得线程看起来可以通过消息通信(比如Scala的Actor)
可以通过消息传递,但Actor本身和线程是有很大不同,不过看起来具备了一些线程功能 。
Java内存模型
线程间如何通信以及如何同步?
线程通信
通信是指线程之间以何种机制来交换信息。在命令式编程中,线程之间的通信机制有两种:共享内存和消息传递。
-
共享内存
在共享内存的并发模型里,线程之间共享程序的公共状态,通过写-读内存中的公共状态进行隐式通信。
- 消息传递
线程之间没有公共状态,线程之间必须通过发送消息来显式进行通信。
线程同步
同步是指程序中用于控制不同线程间操作发生相对顺序的机制。
有点拗口,我的理解就是多线程之间存在并发问题,线程之间很难保持顺序,就像车道上的汽车,有些快有些慢,同步就是由多车道变为单车道,使线程并行执行改为串行执行,串行后能保证线程的顺序。
在共享内存并发模型里,同步是显式进行的。程序员必须显式指定某个方法或某段代码需要在线程之间互斥执行。
就是使用synchronized或者ReentrantLock。
在消息传递的并发模型里,由于消息的发送必须在消息的接收之前,因此同步是隐式进行的。
Java的并发采用的是共享内存模型,Java线程之间的通信总是隐式进行,整个通信过程对程序员完全透明。
内存模型
java的堆内存是线程共享的。堆内存中存放着类的实例域、静态域和数组元素。
图不一定正确,只是按目前的理解画的,要看完《深入了解Java虚拟机》才能对内存模型有更深入的理解。
局部变量(Local Variables),方法定义参数(Java语言规范称之为Formal Method Parameters)和异常处理器参数(Exception Handler Parameters)不会在线程之间共享,它们不会有内存可见性问题,也不受内存模型的影响。
Java线程之间的通信由Java内存模型本文简称为JMM)控制,JMM决定一个线程对共享变量的写入何时对另一个线程可见。
线程之间的共享变量存储在主内存(Main Memory)中,每个线程都有一个私有的本地内存(Local Memory),本地内存中存储了该线程以读/写共享变量的副本。
抽象结构示意图(出自《Java并发编程艺术》)
线程A与线程B通信,线程A与线程B都会在各自的本地内存中缓存一份主内存中的共享变量副本,线程A修改共享变量的值,线程B会感知到主内存中的共享变量发生变化,从而将修改后的值重新读取到线程B的本地内存中从而达到线程通信的目的。
重排序
重排序是指编译器和处理器为了优化程序性能而对指令序列进行重新排序的一种手段。
1)编译器优化重排序:
在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。
2)指令级并行的重排序:
现代处理器采用了指令级并行技术(Instruction-LevelParallelism,ILP)来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。
3)内存系统的重排序:
由于处理器使用缓存和读/写缓冲区,这使得加载和存储操作看上去可能是在乱序执行。
Java编译器在生成指令序列时,插入特定类型的内存屏障(Memory Barriers,Intel称之为Memory Fence)指令,通过内存屏障指令来禁止特定类型的处理器重排序。
数据依赖性
两个操作访问同一个变量,且这两个操作中有一个为写操作,此时这两个操作之间就存在数据依赖性。而重排序是不会改变存在数据依赖关系的两个操作的执行顺序的。
数据依赖性
happens-before
在JMM中,如果一个操作执行的结果需要对另一个操作可见,那么这两个操作之间必须要存在happens-before关系。这里提到的两个操作既可以是在一个线程之内,也可以是在不同线程之间。
happens-before只是描述内存可见性的,不是指前一个操作必须在后一个操作之前执行。
- 程序顺序规则:一个线程中的每个操作,happens-before于该线程中的任意后续操作。
- 监视器锁规则:对一个锁的解锁,happens-before于随后对这个锁的加锁。
- volatile变量规则:对一个volatile域的写,happens-before于任意后续对这个volatile域的读。
- 传递性:如果A happens-before B,且B happens-before C,那么A happens-before C。
参考:
《Java编发编程艺术》
以上是关于java的线程之间如何进行消息传递的主要内容,如果未能解决你的问题,请参考以下文章