wait/notify机制
线程与线程之间不是独立个体,它们彼此是可以互相通信和协作的。
不使用wait/notify机制实现线程间通信
可以使用sleep()和while(true)死循环法来实现多个线程间的通信
wait/notify机制
wait/notify机制的原理
拥有相同锁的线程才能实现该机制
wait()方法的基本使用
该方法是Object类的方法,仅限锁对象调用。
完整实现wait/notify机制
线程状态的切换
new Thread()新建线程,调用start()方法到运行,调用stop()方法销毁。运行状态调用yield()出让cpu资源,调用suspend()、sleep()、wait()方法暂停线程,调用stop()、run()方法销毁。暂停状态调用resume()方法到运行,调用stop()方法销毁。
wait()方法:立即释放锁
sleep()方法:不释放锁
notify()方法:不立即释放锁
interrupt()方法遇到wait()方法
线程调用wait()方法后再对该线程对象执行interrupt()方法会出现Interrupted-Exception异常。
notify()方法:只通知一个线程
每调用一次notify()方法,只通知一个线程进行唤醒,唤醒的顺序与执行wait()方法的顺序一致。
notifyAll()方法:通知所有线程
wait(long)方法的基本使用
带一个参数的wait(long)方法的功能是等待某一时间内是否有线程对锁进行notify()通知唤醒,如果超过这个时间则线程自动唤醒,能继续向下运行的前提是再次持有锁。
wait(long)方法自动向下运行需要重新持有锁
必须有锁才能向下运行,否则一直等待,直到有锁为止。
通知过早问题与解决方法
新增一个变量来控制通知顺序,由通知线程管理。
wait条件发生变化与使用while的必要性
多线程会导致消费异常,需要使用while保证程序正确
生产者/消费者模式的实现
解决假死:使用notifyAll()方法通知所有的类
通过管道进行线程间通信—字节流
import java.io.PipedInputStream;
/**
* @author 29467
* @date 2022/9/7 22:51
*/
public class ReadData {
public void readMethod(PipedInputStream input){
try{
System.out.println("read:");
byte[] byteArray = new byte[20];
int readLength = input.read(byteArray);
while(readLength != -1){
String newData = new String(byteArray,0,readLength);
System.out.println(newData);
readLength = input.read(byteArray);
}
System.out.println();
input.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
import java.io.IOException;
import java.io.PipedOutputStream;
/**
* @author 29467
* @date 2022/9/7 22:44
*/
public class WriteData {
public void writeMethod(PipedOutputStream out){
try {
System.out.println("write:");
for(int i = 0; i<300;i++){
String outData = "" + (i+1);
out.write(outData.getBytes());
System.out.println(outData);
}
System.out.println();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
import java.io.PipedInputStream;
/**
* @author 29467
* @date 2022/9/7 23:10
*/
public class ThreadRead extends Thread{
private ReadData read;
private PipedInputStream input;
public ThreadRead(ReadData read, PipedInputStream input) {
super();
this.read = read;
this.input = input;
}
@Override
public void run() {
read.readMethod(input);
}
}
import java.io.PipedOutputStream;
/**
* @author 29467
* @date 2022/9/7 23:07
*/
public class ThreadWrite extends Thread{
private WriteData writeData;
private PipedOutputStream out;
public ThreadWrite(WriteData writeData, PipedOutputStream out){
super();
this.writeData = writeData;
this.out = out;
}
@Override
public void run() {
writeData.writeMethod(out);
}
}
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
/**
* @author 29467
* @date 2022/9/7 23:12
*/
public class Run {
public static void main(String[] args) {
try{
WriteData writeData = new WriteData();
ReadData readData = new ReadData();
PipedInputStream inputStream = new PipedInputStream();
PipedOutputStream outputStream = new PipedOutputStream();
outputStream.connect(inputStream);
ThreadRead threadRead = new ThreadRead(readData,inputStream);
threadRead.start();
Thread.sleep(2000);
ThreadWrite threadWrite = new ThreadWrite(writeData,outputStream);
threadWrite.start();
}catch (IOException e){
e.printStackTrace();
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
通过管道进行线程间通信—字符流
实现wait/notify的交叉备份
利用notifyAll()和volatile变量来实现交叉备份
join()方法的使用
他的作用是等待线程对象销毁,会使得当前线程暂停等待(内部是wait()方法)
join()方法和interrupt()方法出现异常
join()和interrupt()方法彼此遇到会出现异常,不论先后
join(long)方法的使用
参数用于设置等待的时间,不管x线程是否执行完毕,时间到了并且重新获得了锁,则当前线程会继续向后运行。如果没有重新获得锁,则一直在尝试,知道获得锁为止。
join(long)方法与sleep(long)方法的区别
join(long)方法的功能是在内部是使用wait(long)方法来进行实现的,所以join(long)方法具有释放锁的特点
join()方法后面的代码提前运行—出现意外
原因:线程run()方法的随机性
join(long millis,int nanos)方法的使用
参数为毫秒和纳秒
类ThreadLocal的使用
一个ThreadLocal对象对应一个value
get()方法与null
如果ThreadLocal没有对应的value值则返回null,如果有值则get()方法返回对应的值。
类ThreadLocal存取数据流程分析
ThreadLocal将值存到ThreadLocalMap中
验证线程变量的隔离性
线程可以从ThreadLocal里取出自己的值,不能取出其他线程的值
解决get()方法返回null的问题
重写ThreadLocal的initialValue()方法,返回的值为默认值。
类InheritableThreadLocal的使用
使用该类可以使子线程继承父线程的值
类ThreadLocal不能实现值继承
要继承的话用类InheritableThreadLocal
值继承特性在源代码中的执行流程
详见源代码