首先需要知道线程的几种状态以及wait()和notify()方法的使用
线程的几种状态
NEW(新建): 线程刚被创建,但是并未启动。还没调用start方法。
Runnable(可运行): 线程可以在java虚拟机中运行的状态,可能正在运行自己代码,也可能没有,这取决于操作系统处理器。
Blocked(锁阻塞/阻塞): 当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状态;当该线程持有锁时,该线程将变成Runnable状态。
Waiting(无限等待):一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态。进入这个状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒。
Timed Waiting(计时等待): 同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。这一状态将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有Thread.sleep 、Object.wait(注意:wait可以实现计时等待和无限等待)。
Teminated(被终止) 因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡。
wait()和notify()细节
1.wait方法与notify方法必须要由同一个锁对象调用。因为:对应的锁对象可以通过notify唤醒使用同一个锁对象调用的wait方法后的线程。
2.wait方法与notify方法是属于Object类的方法的。因为:锁对象可以是任意对象,而任意对象的所属类都是继承了Object类的。
3.wait方法与notify方法必须要在同步代码块或者是同步函数中使用。因为:必须要通过锁对象调用这2个方法。
什么是等待唤醒机制:
这是多个线程间的一种协作机制。谈到线程我们经常想到的是线程间的竞争(race),比如去争夺锁,但这并不是故事的全部,线程间也会有协作机制。就好比在公司里你和你的同事们,你们可能存在在晋升时的竞争,但更多时候你们更多是一起合作以完成某些任务。
通俗的讲就是:就是在一个线程进行了规定操作后,就进入等待状态(wait()), 等待其他线程执行完他们的指定代码过后 再将其唤醒(notify());在有多个线程进行等待时, 如果需要,可以使用 notifyAll()来唤醒所有的等待线程
经典问题:生产者和消费者
例: 生产一个产品A,B,C,D,E五个产品,生产者生产,消费者消费,生产者不产出,消费者无法消费
思路: 首先生产者和消费者是两个不同的线程,这两个线程去争抢,有以下几种情况:
1.如果先是生产者抢到,则产出产品,消费者抢到,则消费者进行消费;
2.如果先是生产者抢到,则产出产品,第二次还是生产者抢到,但并没有去消费的时候,则此生产者将进入无限等待状态,再去争抢,此时只用消费者,消费者消费,并唤醒线程
3.如果先是消费者抢到,此时没用产品可消费,则进入无限等待,再去争抢,此时只有生产者,生产者生产产品,并唤醒线程,消费者被唤醒后去消费
public class ProducerConsumerSyn {
public static void main(String[] args) {
//产品数组
String[] arr={"A","B","C","D","E"};
//创建产品类对象
Product p= new Product(arr);
//创建生产者和消费者类对象
Producer producer = new Producer(p);
Consumer consumer = new Consumer(p);
//创建线程对象
Thread producerThread=new Thread(producer,"生产者");
Thread consumerThread=new Thread(consumer,"消费者");
//开启线程
producerThread.start();
consumerThread.start();
}
}
//生产的产品类
class Product{
private String[] arr;//产品数组
private String product;//产品
private boolean status=false;//是否产出产品,,默认没有产品
public Product(String[] arr) {
this.arr = arr;
}
//生产者调动该方法,生产A-E
public synchronized void setProduct(String[] arr) {
//如果有产品,进入无限等待
if(this.status){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果没有产品,则生产产品,
Random random = new Random();
int i = random.nextInt(arr.length);
this.product = arr[i];
System.out.println(Thread.currentThread().getName()+"此时生产的产品为:"+this.product);
//将状态改为true 有产品
this.status=true;
//如果没有生产就进入消费者,消费者线程会进入无限等待,需要唤醒
this.notify();
}
//消费中调用该方法,消费产品
public synchronized String getProduct() {
//如果没有产品,进入无限等待
if(!this.status){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果有产品产出,则去消费产品
System.out.println(Thread.currentThread().getName()+"此时消费的产品为:"+this.product);
//睡眠3秒
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//将状态改回成false
this.status=false;
//如果连续两次进入生产者线程,生产者线程会进入无限等待,需要唤醒
this.notify();
return product;
}
public String[] getArr() {
return arr;
}
public void setArr(String[] arr) {
this.arr = arr;
}
}
//生产者类
class Producer implements Runnable{
private Product product;
public Producer(Product product) {
this.product = product;
}
@Override
public void run() {
while (true) {
product.setProduct(product.getArr());
}
}
}
//消费者类
class Consumer implements Runnable{
private Product product;
public Consumer(Product product) {
this.product = product;
}
@Override
public void run() {
while (true) {
product.getProduct();
}
}
}