多线程间通信
同步:多个线程对同一个资源的相同操作,同步即保证数据安全。
通信:多个线程对同一个资源(共享资源)不同操作。需要进行通信,保证数据安全问题
-
多个线程使用同一个run方法,通过synchronize锁资源实现同步
-
多个线程使用的不同的run方法,即生产者和消费者,通过通信来保证安全问题
-
生产者与消费者模式
多线程通信---生产者和消费者。 考虑线程安全问题
- 生产者:发布资源。如写
- 消费者:利用资源。如读
package com.xiaoai.thread;
/**
* 生成者消费者
*/
//资源
class Res {
public String userName;
public String sex;
}
//生产
class Out extends Thread{
Res res;
public Out(Res res){
this.res = res;
}
@Override
public void run() {
//写操作
int count=0;
while (true){
if (count==0){
res.userName = "xiaoai"; //1--生产线程到这里改变了资源姓名,还未修改性别,同时消费线程直接执行打印了资源信息
res.sex = "男";
}else {
res.userName = "honghong";
res.sex="女";
}
//计算奇数或偶数 使上面写出不同数据
count = (count+1)%2;
}
}
}
//消费
class Input extends Thread{
Res res;
public Input(Res res){
this.res = res;
}
@Override
public void run() {
while (true){
//2--消费线程同时运行,打印了userName=xiaoai,但是生成线程还没有修改好性别,此时sex=女,然后这里直接打印了,所以出现了:xiaoai--女
System.out.println(res.userName+"--"+res.sex);
}
}
}
public class OutInputThread {
public static void main(String[] args){
Res res = new Res();
Out out = new Out(res); //生产线程
Input input = new Input(res); //消费线程
out.start();
input.start();
}
}
- 解决1:要使用同一锁 用this锁不行,因为不是同一个run即不是同一个this 锁同一个资源(即res)可以,但是有点问题。即不会写一个读一个,或者会重复读取一个相同的数据
- 解决2:生产一个,消费一个,没有生产即不可消费,消费没完则不可生产
wait():让当前线程从运行状态变为休眠状态 即:立即释放锁的资源
notify():让当前线程从休眠状态变为运行状态 即:唤醒另一个线程 必须是同一个锁的资源才能唤醒
notifyAll():唤醒所有等待中的线程
ps:需要同步才能使用,而且要是同一个锁的资源 一般wait()和notify()一起使用 wait和notify只能在synchronized使用
package com.xiaoai.thread;
/**
* 生成者消费者
*/
//资源 ---包子
class Res {
public String pi; //包子皮
public String xian; //馅料
//标志位 true==生产者线程进行等待,消费者可以消费 false==生产者线程进行生产,消费者线程进行等待
public boolean flag = false; //有资源=true 没有资源=false
}
//生产者
class Out extends Thread{
Res res;
public Out(Res res){
this.res = res;
}
@Override
public void run() {
//生产操作
int count=0;
while (true){
synchronized (res){
if (res.flag){ //flag==true 有资源,无法生产,所以生产者需要等待
try {
res.wait(); //表示让当前线程从运行状态变成休眠状态 并且释放锁的资源
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (count==0){
res.pi = "薄皮"; //1--生产线程到这里改变了皮,还未修改馅,同时消费线程直接执行打印了资源信息
res.xian = "猪肉馅";
}else {
res.pi = "冰皮";
res.xian="绿豆馅";
}
count = (count+1)%2; //计算奇数或偶数 实现生产不同数据
System.out.println("生产者生产了-"+res.pi+res.xian+"-包子");
//----------生产完成,可以消费
System.out.println("-----------生产了-"+res.pi+res.xian+"-包子,可以消费");
res.flag = true;//修改标志位,表示写完了,有资源了,提示消费者消费
res.notify(); // 唤醒消费者线程
}
}
}
}
//消费者
class Input extends Thread{
Res res;
public Input(Res res){
this.res = res;
}
@Override
public void run() {
//消费操作
while (true){
synchronized (res){
if (!res.flag){ //flag==false 没有资源,无法消费,所以消费者需要等待
try {
res.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//2--消费线程同时运行,打印了皮=薄皮,但是生成线程还没有修改好馅,此时性别还为绿豆馅,然后这里直接打印了,所以出现了:薄皮绿豆馅包子
System.out.println("消费者消费了-"+res.pi+res.xian+"-包子");
//----------消费完了,可以生产
System.out.println("-----------消费了-"+res.pi+res.xian+"-包子,可以生产");
System.out.println("------------------------------------------------------
");
res.flag = false; //修改标志位,告知消费完了,没有资源了消费了,提醒生产者生产
res.notify(); //唤醒生产者线程
}
}
}
}
public class OutInputThread {
public static void main(String[] args){
Res res = new Res();
Out out = new Out(res);
Input input = new Input(res);
out.start();
input.start();
}
}
- wait和sleep的区别?
作用都是做休眠
wait用于同步中可以释放锁的资源,sleep不会释放锁的资源,sleep只有等时间到期才从休眠状态变为运行状态。
wait需要notify才能从休眠状态变为运行状态