多线程之间的通信,其实就是多个线程同时操作(读+写)同一个资源。
安全问题:
当线程在读取资源的过程中,写线程操作了资源,
导致读线程读取的数据,一部分是写之前的数据,一部分是写之后的数据。
解决安全问题:
读线程和写线程使用同一把对象锁就好了。
code of demo:
package cn.qbz.thread;
/**
* 线程间通信
*/
public class ConnectThreadTest {
public static void main(String[] args) {
Student student = new Student();
ProduceTest produceTest = new ProduceTest(student);
ConsumerTest consumerTest = new ConsumerTest(student);
produceTest.start();
consumerTest.start();
}
}
class ProduceTest extends Thread {
private Student student;
public ProduceTest(Student student) {
this.student = student;
}
@Override
public void run() {
int num = 1;
while (true) {
synchronized (student) {
//如果可以生产,生产者生产
if (student.getCanProduce()) {
if (num == 1) {
student.setAge(6);
student.setName("小王");
num = 0;
} else {
student.setName("老王");
student.setAge(99);
num = 1;
}
//重置生产者不可以生产
student.setCanProduce(false);
}
student.notifyAll();
try {
student.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
class ConsumerTest extends Thread {
private Student student;
public ConsumerTest(Student student) {
this.student = student;
}
@Override
public void run() {
while (true) {
synchronized (student) {
//如果不可以生产,进行消费
if (!student.getCanProduce()) {
//重置生产者可以生产
student.setCanProduce(true);
System.out.println(student.toString());
}
student.notifyAll();
try {
student.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
class Student {
private String name;
private Integer age;
private Boolean canProduce = false;
public Boolean getCanProduce() {
return canProduce;
}
public void setCanProduce(Boolean canProduce) {
this.canProduce = canProduce;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
wait 和 sleep
共同点: 都是休眠,都释放CPU资源
不同点:
wait必须用于同步中,释放锁,sleep哪里都可以用,且不释放锁。
wait必须使用notify或notifyAll来唤醒,sleep时间到了就会唤醒。