Java中线程通讯
主要涉及对并发线程的操控
1.利用synchronized进行对象锁定实现对线程的控制
2.使用lock condition进行线程控制
3.使用管道(Piped)Stream
为什么要用线程通讯,常见的就是需要访问同一个对象,根据对象的值进行相关的操作
举个例子
小王是个程序员,今天收到客户A反馈的线上bug需要修改,产品经理B告诉小王页面也有点问题需要调整。
小王同时只能干一件事,要么改bug,要么调整页面。那么小王怎么安排呢。
小王考虑了一下想到自己的方案,先把bug修改完成,等待客户反馈,然后修改页面,改完以后交于产品确认。
等待客户反馈的时间小王可以先去修改页面,但是如果客户反馈有问题,小王需要继续修复bug
-
synchronized 实现
可以定义线程了
FixBugThread
public class FixBugThread2 implements Runnable {
private Object person;
public FixBugThread2(Object person){
this.person = person;
}
@Override
public void run() {
synchronized (person)
{
System.out.println("*********修复bug中********");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("*********bug修复完成********");
person.notify();
}
}
}
MakePageThread
public class MakePageThread2 implements Runnable {
private Object person;
public MakePageThread2(Object person) {
this.person = person;
}
@Override
public void run() {
synchronized (person) {
System.out.println("*********页面调整中********");
for (int i = 0; i < 10; i++) {
System.out.println("*********页面调整中" + i*10 + "% ********");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("*********页面调整完成********");
person.notify();
}
}
}
定义完以后 我们跑一下
public static void main(String[] args) throws InterruptedException {
System.out.println("开始运行");
Object a = new Object();
FixBugThread2 fbt = new FixBugThread2(a);
MakePageThread2 mpt = new MakePageThread2(a);
new Thread(fbt).start();
new Thread(mpt).start();
System.out.println("结束运行");
}
运行结果:
开始运行
*********修复bug中********
*********bug修复完成********
*********页面调整中********
*********页面调整中0% ********
*********页面调整中10% ********
*********页面调整中20% ********
*********页面调整中30% ********
*********页面调整中40% ********
*********页面调整中50% ********
*********页面调整中60% ********
*********页面调整中70% ********
*********页面调整中80% ********
*********页面调整中90% ********
*********页面调整完成********
结束运行
但是这是理想情况,小王一次性把bug解决掉了,实际情况是小王修改完bug以后,在调整页面的过程过客户又找来了,bug没有完全修复,小王需要再次修改bug。那怎么处理呢?我们需要终止makePage线程,然后小王再次修改bug
所以对于调整页面的线程类方法我们要加上自己的控制
public class MakePageThread2 implements Runnable {
private Object person;
private Boolean isEnabled = true;
private Integer runTime = 10;
private Integer currentRunCount = 0;
public MakePageThread2(Object person) {
this.person = person;
}
@Override
public void run() {
/* while (true)
System.out.println("**************MakePage");*/
synchronized (person) {
System.out.println("*********页面调整中********");
while (true)
{
while (isEnabled)
{
System.out.println("*********页面调整中" + currentRunCount*10 + "% ********");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
currentRunCount++;
if( currentRunCount >=runTime)
{
person.notify();
break;
}
}
if(!isEnabled)
{
try {
person.wait(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if( currentRunCount >=runTime)
{
person.notify();
break;
}
}
System.out.println("*********页面调整完成********");
person.notify();
}
}
public void stop()
{
isEnabled = false;
System.out.println("*********页面调整被打断********");
}
public void continueRun()
{
isEnabled = true;
}
}
这块代码主要目的是就是一直运行知道完成页面调整,如果中间外部通知需要打断,就暂停运行。等待外部通知。
所以 对应main中的调用 我就改成了如下代码
System.out.println("开始运行");
Object a = new Object();
FixBugThread2 fbt = new FixBugThread2(a);
MakePageThread2 mpt = new MakePageThread2(a);
Thread fixBugT = new Thread(fbt);
Thread makePageT = new Thread(mpt);
fixBugT.start();//修复bug
makePageT.start();//调整页面
System.out.println("主线程开始休眠");
Thread.sleep(1000);
System.out.println("主线程结束休眠");
mpt.stop();//bug没改好 停止调整页面
new Thread(fbt).start();//重新修改bug
mpt.continueRun();//改完bug以后继续调整页面
-
Lock 实现
使用Condition往往比使用传统的通知等待机制(Object的wait()/notify())要更灵活、高效,例如,我们可以使用多个Condition实现通知部分线程
-
PipedInputStream类 与 PipedOutputStream类