4. 线程间通信
一.线程通信
1.等待与通知机制
1.1 不适用等待/通知机制实现线程之间通信
public class CommunicateWhile {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
ThreadA tha = new ThreadA(list);
tha.start();
ThreadB thb = new ThreadB(list);
thb.start();
}
}
class ThreadA extends Thread {
private List<String> list;
public ThreadA(List<String> list) {
this.list = list;
}
public void run() {
for (int i = 0; i < 10; i++) {
list.add(i + "");
System.out.println("a添加了" + (i + 1) + "个元素");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class ThreadB extends Thread {
private volatile List<String> list;
public ThreadB(List<String> list) {
this.list = list;
}
public void run() {
try {
while (true) {
if (list.size() == 5) {
System.out.println("==5了,线程B要退出了");
throw new Exception("InterruptException");
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
这里是通过两个线程操作同样一个List,一个线程改变List的内容,另外一个对象死循环判断,这样会疯狂消耗CPU资源。如果加上死循环的时间消耗可能会得到改善 但是第一时间无法得到准确的值,inert需要等待/通知机制
1.2 等待/通知机制
线程间通信:
大概实现的模型有两种: 等待/通知机制 共享内存
1.最简单的等待/通知机制
一个线程改变值 另外一个线程while(true) 不断获取值 来做操作 确定:while语句轮训 会浪费CPU资源
2.wait()/notify()
wait是Object类的方法 在调用wait方法之前必须得到该对象的锁 当没有获得对象的锁时 会跑出异常
当调用wait方法之后 他会释放对象的锁
notify:对其发出通知 并使它等待获取该对象的对象锁 ps:notify调用以后 不会马上释放对象锁 而是notify的线程走完synchronized以后才会
eg:
等待:
synchronized(lock){
System.out.println("wait before"); //A
lock.wait();
System.out.println("wait after"); //B
}
通知:
synchronized(lock){
System.out.println("notify before); //C
lock.notify();
System.out.println("notify after"); //D
}
如果是等待先通知: 那么顺序是 A CD B 等notify先走完了 才会走B
PS:如果先执行通知部分的代码 那么 你的等待线程将永远不会被唤醒
notifyAll(); 可以将wait状态的所有线程全都唤醒 正常来说是优先级最高的线程先执行 当然 这个取决于虚拟机的配置
如果调用notify 那么将会是随机被唤醒
如果被等待的锁定的对象 在别的地方调用了 interrupt方法 lock.interrupt() 那么wait的线程将会跑出异常
wait(long):超过一定的等待时间 如果没被唤醒 那么将会自动唤醒
3.管道实现通信 管道通信流又分为字符流和字节流
管道流是一种特殊的流 用于不同的线程中直接传送数据
PipeInputStream和PipeOutputStream 字节流
PipeRead和PipeWrite 字符流
字节流代码:
public class ThreadRead implements Thread{
private PipedInputStream pipedInputStream;
public ThreadRead(PipedInputStream pipedInputStream){
this.pipedInputStream = pipedInputStream;
}
@Override
public void run(){
try{
System.out.println("read :");
byte[] byteArray = new byte[20];
while((readLength = input.read(byteArray)) != -1){ //如果没有数据 会被阻塞在这一行 如果有数据则会读取
String newData = new String(byteArray,0,readLength);
System.out.print(newData);
}
}
}
}
public class ThreadWrite implements Thread{
private PipedInputStream pipedOutputStream;
public ThreadWrite(PipedInputStream pipedOutputStream){
this.pipedOutputStream = pipedOutputStream;
}
@Override
public void run(){
try{
System.out.println("write:");
for(int i = 0; i<20; i++){
String outData = "" + (i + 1);
out.write();
System.out.print(outData);
}
out.close();
}
}
}
其实就是一个线程读 一个线程写 只不过是传入了这个输入输出流
public static void main(String []args){
try{
PepedInputStream input = new PipedInputStream();
PepedOutputStream out = new PipedOutputStream();
out.connect(input); //或者input.conect(out);
Thread threadRead = new ThreadRead();
threadRead.start();
Thread threadWrite = new ThreadWrite();
thread.write();
}
}
以下是一个交替打印的例子
package com.zll.volidate;
public class Print {
public static void main(String[] args) {
PrintControl printControl = new PrintControl();
Thread threadPrint1 = new Thread(new ThreadPrint(printControl, 0));
Thread threadPrint2 = new Thread(new ThreadPrint(printControl, 1));
threadPrint1.start();
threadPrint2.start();
}
}
class PrintControl {
int threadACount = 1;
int threadBCount = 64; //A前一位
volatile boolean flag = false;
synchronized public void printA() {
try {
while(true){
while (flag == true) {
wait();
}
System.out.println(threadACount++);
Thread.sleep(1000);
flag = true;
notify();
}
} catch (Exception e) {
e.printStackTrace();
}
}
synchronized public void printB() {
try {
while(true){
while (flag == false) {
wait();
}
System.out.println( (char)++threadBCount);
Thread.sleep(1000);
// threadBCount += 1;
flag = false;
notify();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
class ThreadPrint implements Runnable {
PrintControl printControl;
int control;
public ThreadPrint(PrintControl printControl, int control) {
this.printControl = printControl;
this.control = control;
}
@Override
public void run() {
if (control == 0) {
printControl.printA();
} else {
printControl.printB();
}
}
}
join的用法: 比如你的主线程需要等到子线程跑完了 在继续执行 那么就可以使用join
join可使线程排队 有类似同步的方法 他跟synchronized的区别 join在内部是使用wait方法进行等待
join(long):设定等待的时间
一个子线程
int dealTime = (int)(Math.random() * 10000);
System.out.println(dealTime);
Thread.sleep(dealTime);
主线程
public static void main(String[] args) {
try{
ChildThread childThread = new ChildThread();
childThread.join();
childThread.start();
System.out.println("last "); //此处的last总是在childThread执行完成以后
}catch (Exception e){
e.printStackTrace();
}
System.out.println("last ");
}

浙公网安备 33010602011771号