1 案例:生产与消费
生产与消费同时执行(多线程),但是执行的任务不同,处理的资源一样。生产者每生产一个消费者就消费一个。
package com.java.example;
// 测试类
public class Thread01 {
public static void main(String[] args) {
//1.创建资源对象
Resource r = new Resource();
//2.创建线程任务,操作同一资源对象
Producer p = new Producer(r);
Consumer c = new Consumer(r);
//3.创建线程
Thread t1 = new Thread(p);//负责生产
Thread t2 = new Thread(c);//负责消费
//4.启动线程
t1.start();
t2.start();
}
}
/*描述资源(面包)
* 希望的结果是:生产一个面包就要消费一个面包
* 什么情况下生产,什么情况下消费?
* 当有面包时,则消费,当没有面包时,则生产
* 定义标记来标记当前是否有面包
* (1)如果标记为true,代表有面包,生产者先告诉消费者来消费,然后再进入到等待状态。
* (2)如果标记为false,代表没有,消费者先告诉生产者来生产,然后再去等待*/
class Resource{
//属性
private String name; //面包的名字
private int count = 1; //编号
// 定义一个标记
Boolean flag = false;
//set()方法:对面包名字赋值
public synchronized void set(String name){
//flag为true,有面包,则进入等待状态
if(flag){
try {wait();} catch (InterruptedException e) {}
}
// flag为false,没面包,生产者要进行生产。
this.name = name+count;
count++;
System.out.println(Thread.currentThread().getName()
+"---生产者---"+this.name);
// 把标记改为true
flag = true;
// 唤醒消费者
notify();
}
//get()方法:获取面包
public synchronized void get(){
// 如果flag为false,没有面包,则等进入等待状态
if(!flag){
try {wait();} catch (InterruptedException e) {}
}
// flag为ture,有面包,消费者进行消费
System.out.println(Thread.currentThread().getName()
+"===消费者==="+this.name);
// 把标记改为false
flag = false;
// 唤醒生产者
notify();
}
}
//描述生产者,具备自己的任务
class Producer implements Runnable{
//要保证生产和消费处理的是一个资源对象,
//就不能Resource r = new Resource();
//解决方法:向方法中直接传参(参数:资源)
private Resource r;
public Producer(Resource r){
this.r = r;
}
@Override
public void run() {
//生产面包
while(true){
r.set("资溪面包");
}
}
}
//描述消费者,具备自己的任务
class Consumer implements Runnable{
private Resource r;
public Consumer(Resource r){
this.r = r;
}
@Override
public void run() {
//消费面包
while(true){
r.get();
}
}
}
分析:
如果flag为true,有面包。生产者线程wait(),消费者消费,修改标识并唤醒生产者线程,生产者生产面包,消费者线程wait()。就这样保证了生产一个消费一个。
2 等待唤醒机制
可以改变线程状态。
(1)wait():等待,让线程释放执行资格以及执行权,将线程临时存到线程池中,等待着被唤醒。
(2)notify():唤醒线程池中任意一个等待的线程
(3)notifyAll():会唤醒线程池中所有的等待线程
总结:这些方法必须使用在同步中,因为必须要标识wait,notify 等方法所属的锁,同一锁上的notify 只能够唤醒该锁上被wait的线程,
为什么这些方法是操作线程的,但是却定义在了Object类中呢?
因为这些方法必须要标识所属的锁是哪个对象,锁可以是任意对象类型,任意对象可以调用的方法就肯定是object类中的方法。