一、为什么要线程通信?
1. 多个线程并发执行时, 在默认情况下CPU是随机切换线程的,当我们需要多个线程来共同完成一件任务,
并且我们希望他们有规律的执行, 那么多线程之间需要一些协调通信,以此来帮我们达到多线程共同操作一份数据。
2.当然如果我们没有使用线程通信来使用多线程共同操作同一份数据的话,虽然可以实现,
但是在很大程度会造成多线程之间对同一共享变量的争夺,那样的话势必为造成很多错误和损失!
3.所以,我们才引出了线程之间的通信,多线程之间的通信能够避免对同一共享变量的争夺。
二、什么是线程通信?
多个线程在处理同一个资源,并且任务不同时,需要线程通信来帮助解决线程之间对同一个变量的使用或操作。
就是多个线程在操作同一份数据时, 避免对同一共享变量的争夺。
于是我们引出了等待唤醒机制:(wait()、notify())
就是在一个线程进行了规定操作后,就进入等待状态(wait), 等待其他线程执行完他们的指定代码过后 再将其唤醒(notify);
(1)wait()方法:
在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。
线程调用wait()方法,释放它对锁的拥有权,然后等待另外的线程来通知它(通知的方式是notify()或者notifyAll()方法),这样它才能重新获得锁的拥有权和恢复执行。
要确保调用wait()方法的时候拥有锁,即,wait()方法的调用必须放在synchronized方法或synchronized块中。
(2)notif()方法:
notify()方法会唤醒一个等待当前对象的锁的线程。唤醒在此对象监视器上等待的单个线程。
(3)notifAll()方法:
notifyAll()方法会唤醒在此对象监视器上等待的所有线程。
(4)如果多个线程在等待,它们中的一个将会选择被唤醒。这种选择是随意的,和具体实现有关。(线程等待一个对象的锁是由于调用了wait方法中的一个) notify()方法应该是被拥有对象的锁的线程所调用。
(5)以上方法都定义在类:Object中 1.因为,这些方法在操作同步中的线程的时候,都必须标示其所操作线程所持有的锁(被该锁的对象调用), 而只有同一个对象监视器下(同一个锁上)的被等待线程,可以被持有该锁的线程唤醒,(无法唤醒不同锁上的线程) 2.所以,等待和唤醒的必须是同一个对象的监视器①下(同一个锁上)的线程。
而锁可以是任意已近确定的对象, 能被任意对象调用的方法应当定义在 Object类中。 注:
①
监视器(锁):同一个对象的监视器下(同一个锁上)的线程,一次只能执行一个:就是拥有监视器所有权(持有锁)的那一个线程。
多线程之间通讯,其实就是多个线程在操作同一个资源,但是操作的动作不同。
wait()、notify、notifyAll()方法
wait()、notify()、notifyAll()是三个定义在Object类里的方法,可以用来控制线程的状态。
这三个方法最终调用的都是jvm级的native方法。随着jvm运行平台的不同可能有些许差异。
如果对象调用了wait方法就会使持有该对象的线程把该对象的控制权交出去,然后处于等待状态。
如果对象调用了notify方法就会通知某个正在等待这个对象的控制权的线程可以继续运行。
如果对象调用了notifyAll方法就会通知所有等待这个对象控制权的线程继续运行。
注意:一定要在线程同步中使用,并且是同一个锁的资源
package com.jinglitong.shop.entity;
import lombok.Data;
@Data
public class Breads {
//面包的id
private int bid;
//面包的个数
private int num;
//生产面包的方法(由于是demo,方便大家理解,就把synchronized关键字加到方法上面了哦)
public synchronized void produc(){
//当面包的数量不为0时,该方法处于等待状态
if(0 != num){
try {
wait();//等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//当面包数量为0时,那么就开始生产面包了哦
num = num +1;//数量加1
bid = bid + 1 ;//id当然也得加1
String threadname = Thread.currentThread().getName();
System.out.println(threadname+"生产了一个编号为"+bid+"的面包!");
notify();//当执行完后,去唤醒其他处于等待的线程
}
//消费面包的方法
public synchronized void consume(){
//当面包的数量为0时,该方法处于等待状态
if(num == 0 ){
try {
wait();//等待
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//消费完面包了,所以面包数量降为0了
num = num -1;//数量减1
String name1 = Thread.currentThread().getName();
System.out.println(name1+"买了一个面包编号为"+bid);
notify();//当执行完后,去唤醒其他处于等待的线程
}
}
package com.jinglitong.shop.MultithreadCommunication;
import com.jinglitong.shop.entity.Breads;
public class producer extends Thread{
//获得面包的类
private Breads bre ;
//无参构造
public producer() {
super();
}
//有参构造
public producer(Breads bre) {
super();
this.bre = bre;
}
//set和get方法
public Breads getBre() {
return bre;
}
public void setBre(Breads bre) {
this.bre = bre;
}
//继承重写run方法
@Override
public void run() {
pro();
}
//生产面包
private void pro() {
// 本系统默认循环生产20个面包
for (int i = 0; i <20; i++) {
try {
//沉睡0.3秒(演示效果需要,可以不加)
Thread.currentThread().sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
//调用面包类里的生产面包的方法
bre.produc();
}
}
}
package com.jinglitong.shop.MultithreadCommunication;
import com.jinglitong.shop.entity.Breads;
public class consume extends Thread{
//获得面包的类
private Breads bre ;
//set和get方法
public Breads getBre() {
return bre;
}
public void setBre(Breads bre) {
this.bre = bre;
}
//继承重写run方法
@Override
public void run() {
con();
}
//消费面包
private void con() {
// 与生产者保持一致,本系统默认循环生产20个面包(生产几个,消费几个)
for(int i = 0;i<20;i++){
try {
//沉睡0.3秒(演示效果需要,可以不加)
Thread.currentThread().sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
//调用面包类里的生产面包的方法
bre.consume();
}
}
//有参构造
public consume(Breads bre) {
super();
this.bre = bre;
}
//无参构造
public consume() {
super();
}
}
package com.jinglitong.shop.MultithreadCommunication;
import com.jinglitong.shop.entity.Breads;
public class TestBreads {
public static void main(String[] args) {
//new一个面包类
Breads bre = new Breads();
//new一个生产者类
producer proth = new producer(bre);
//new一个消费者类
consume conth = new consume(bre);
//new一个包含消费者类的线程
Thread t1 = new Thread(proth,"生产者");
//new一个包含生产者类的线程
Thread t2 = new Thread(conth,"消费者");
//启动线程
t1.start();
t2.start();
}
}
wait与sleep区别?
对于sleep()方法,我们首先要知道该方法是属于Thread类中的。而wait()方法,则是属于Object类中的。
sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。
在调用sleep()方法的过程中,线程不会释放对象锁。
而当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备
获取对象锁进入运行状态。 wait 必须在同步模块中使用