总结
- 什么是线程通讯,可以将线程分为生产者线程与消费者线程,生产者线程创建共享数据(写),给消费者线程使用(读),注意是生产一个消费一个
- wait() 方法: 当调用该方法时当前线程进入等待,并释放锁
- notify() 方法: 当调用该方法时,唤醒当前对象锁池中等待的线程, 注意点: wait()与notify()方法一定要在Synchronized方法中执行,并且持有的是同一把锁
- wait()与join()的区别:wait()用在 Synchronized 中, 被wait()的线程需要唤醒,
- wait()与sleep()的区别,sleep指定休眠时间,时间过后自动唤醒,并且sleep不会释放锁
使用 wait() 与 notify() 实现线程通讯的原理
- 生产线程与消费线程持有同一把锁,在一个锁池中
- 生产消费的数据中有代表是否生产完毕消费完毕的标识.
- 生产线程在执行时首先判断当前数据是否有消费,如果以消费,则执行生产方法进行生产,当生产完毕时修改标识,调用notify()方法当前线程释放锁资源,同一个锁池中的线程再次争抢,如果未消费完毕则调用wait(),当前线程释放锁资源进入等待状态
- 与生成线程相同,消费线程通过标识首先判断是否生产完毕,如果生产完毕执行消费方法,消费完毕后修改标识调用notify()释放锁资源…
线程通讯示例
- 创建生产消费的数据类
class Res2 {
public String userSex;//性别
public String userName;//年龄
//线程通讯标识(生成与消费线程,通过判断这个标识来确定
//接下来的执行,是该生产消息还是消费消息
public boolean flag = false;
}
- 创建生产线程
class IntThrad extends Thread {
private Res2 res2;//设置Res2位线程类的私有变量
/*初始化生产线程对象时需要传递Res2对象,在
* 生成线程类中的run方法中需要调用Res2中的方法,
* 进而对Res2的这个方法进行多线程处理*/
public IntThrad(Res2 res2) {
this.res2 = res2;
}
/*run方法,在run方法中通过Res2对象,对该对象的属性进行赋值*/
@Override
public void run() {
int count = 0;
while (true) {
synchronized (res2) {
/*获取res2中flag,如果flag为true时,代表刚刚设置了
* res2中的属性值,需要消费线程先进行消费,
* 通过同步代码块中的锁对象,调用wait()方法,
* 当前生产线程释放锁进入,锁池中等待*/
if (res2.flag) {
try {
// 当前线程变为等待,但是可以释放锁
res2.wait();
} catch (Exception e) {
}
}
/*如果falg为false,则是消费线程刚进行了消费,
* 现在要进行生产,通过传入的res2对象对其属性进行赋值
* 如果count为0,则res2是....否则是...*/
if (count == 0) {
res2.userName = "AAA";
res2.userSex = "男";
} else {
res2.userName = "BBB";
res2.userSex = "女";
}
/*count+1然后除以2,永远时两种结果*/
count = (count + 1) % 2;
/*设置flag的值为true,通过这个值判断接下来的操作时生产
* 还是消费,生产是false,消费时true,刚刚生产完,需要进行
* 消费,设置为true*/
res2.flag = true;
// 消息生产完成后,通过锁对象调用notify方法,
// 唤醒一个当前锁池中的一个等待线程,两个线程
//再次通过cpu判断谁先执行
res2.notify();
}
}
}
}
- 创建消费线程
class OutThread extends Thread {
/*将Res2对象设置为消费线程类的私有变量*/
private Res2 res2;
/*在初始化消费线程类对象时,需要传递一个Res2对象
* 在线程类的run方法中通过Res2对象,调用它的方法
* 进而对Res2中的方法实现多线程操作*/
public OutThread(Res2 res2) {
this.res2 = res2;
}
/*消费run方法,方法中调用了Res2中的方法*/
@Override
public void run() {
while (true) {
synchronized (res2) {
/*获取res2中的flag判断,如果不是true,说明
* 刚刚消费完,接下来的操作需要进行生产,通过
* 当前线程的锁对象调用wait()方法,当前消费线程
* 释放锁,进入锁池中等待*/
if (!res2.flag) {
try {
res2.wait();
} catch (Exception e) {
// TODO: handle exception
}
}
/*如果flag为true,说明刚刚生产完,需要进行消费,获取res2中的属性值进行消费*/
System.out.println(res2.userName + "--" + res2.userSex);
/*消费完成后设置flag标志为刚刚消费完,接下来要进行生产*/
res2.flag = false;
/*并通过当前锁对象,调用notify()方法,唤醒当前锁池中
* 的一个等待线程,两个线程再次进行资源争抢*/
res2.notify();
}
}
}
}
- 通讯执行测试
/*线程通信示例
* 注意点:
* 1.两个线程是通过同一个Res2对象创建的
* 2.生产线程与消费线程操作的共享数据要实现同步synchronized
* 3.实现synchronized的两个线程要共用同一把锁
* 4.生产消息与消费消息要生产一个消费一个(不能批量生产,批量消费)
* 5.notify与wait方法要在synchronized中配合使用,并需要同一把锁对象调用
* 6.wait方法,当前线程释放锁进入锁池中等待,再次启用这个线程需要被同一把锁对象调用notify唤醒
* 7.在Res2中声明一个flag属性,通过这个属性判断接下来的操作是该生成还是消费,进而选择当前线程
* 是继续执行还是wait
* 8.虽然消费线程是对共享数据进行读的操作,但是也要实现synchronized,不然会发送错误* */
public class ThreaCommun {
public static void main(String[] args) {
Res2 res2 = new Res2();//创建Res2对象,传递给生产线程对象与消费线程对象
IntThrad intThrad = new IntThrad(res2);//创建生产线程
OutThread outThread = new OutThread(res2);//创建消费线程
intThrad.start();//启动生产线程
outThread.start();//启动消费线程
}
}
使用 Condition 设置线程通讯
- 创建数据类
class Res3 {//共享数据
public String userName;
public String sex;
public boolean flag = false;//执行生产或消费的标识
Lock lock = new ReentrantLock();
}
- 创建生产线程
class InputThread extends Thread {//继承方式创建生产线程类
private Res3 res3;//Res3对象属性
Condition newCondition;//Condition对象属性
//初始化线程类,需要传递Res3对象,与Condition对象
//在线程类的run方法中会使用到两个对象中的成员
public InputThread(Res3 res3, Condition newCondition) {
this.res3 = res3;
this.newCondition=newCondition;
}
@Override
public void run() {//run方法中设置共享数据生产
int count = 0;
while (true) {
// synchronized (res3) {
try {
res3.lock.lock();
if (res3.flag) {
try {
// res3.wait();
newCondition.await();//通过Condition调用await()使当前线程等待
} catch (Exception e) {
}
}
if (count == 0) {
res3.userName = "AAA";
res3.sex = "男";
} else {
res3.userName = "小红";
res3.sex = "女";
}
count = (count + 1) % 2;
res3.flag = true;//修改进程生产还是消费操作的标识
// res3.notify();
newCondition.signal();//通过Condition调用signal(),唤醒一个锁池中的等待线程
} catch (Exception e) {
}finally {
res3.lock.unlock();//需要在finally中手动释放锁
}
}
// }
}
}
- 创建消费线程
class OutThrad extends Thread {//继承方式创建消费线程类
private Res3 res3;
private Condition newCondition;
public OutThrad(Res3 res3,Condition newCondition) {
this.res3 = res3;
this.newCondition=newCondition;
}
@Override
public void run() {//run方法中获取共享数据消费
while (true) {
// synchronized (res3) {
try {
res3.lock.lock();
if (!res3.flag) {
try {
// res3.wait();
newCondition.await();
} catch (Exception e) {
// TODO: handle exception
}
}
System.out.println(res3.userName + "," + res3.sex);
res3.flag = false;//修改标识
// res3.notify();
newCondition.signal();//唤醒锁池中一个等待线程
} catch (Exception e) {
// TODO: handle exception
}finally {
res3.lock.unlock();//需要在finally中手动释放锁
}
// }
}
}
}
- 执行生产与消费
public class ThreadDemo01 {
public static void main(String[] args) {
Res3 res3 = new Res3();
Condition newCondition = res3.lock.newCondition();
InputThread inputThread = new InputThread(res3,newCondition);
OutThrad outThrad = new OutThrad(res3,newCondition);
inputThread.start();
outThrad.start();
}
}