著名的生产者和消费者,如果各只有一个,问题会很简单
package thread;
/*
线程间通讯(间接通信):
其实就是多个线程在操作同一个资源,但是操作的动作不同。
对于此类操作,一般来说可以把共同操作的那个共享资源作为锁(也可以用其他的,只要是用的同一个锁就行)
等待 - 唤醒 机制
sleep() Thread类的静态方法,将当前线程睡眠,不会释放任何资源
wait(); 等待会讲线程放入线程池
notify(); 会唤醒线程池里的等待线程
notifyAll();
wait()的官方解释:
在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程(拥有这个对象的线程)等待。
换句话说,此方法的行为就好像它仅执行 wait(0) 调用一样。
当前线程必须拥有此对象监视器(拥有这个锁)。(执行该方法后)该线程发布对此监视器的所有权(松开这个锁)并等待,
直到其他线程通过调用 notify 方法,或 notifyAll 方法通知在此对象的监视器上等待的线程醒来。
然后该线程将等到重新获得对监视器的所有权(重新获得这个锁)后才能继续执行。
notify()的官方解释:
唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的,并在对实现做出决定时发生。
线程通过调用其中一个 (其持有的锁的)wait 方法,在对象(这个锁的)的监视器上等待。
直到当前线程放弃此对象上的锁定(该方法一般在同步块中执行,当这个线程释放了所之后),才能继续执行被唤醒的线程(在此之前被唤醒的线程未持有锁,无法执行)。
被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争
都使用在同步中,因为要对持有监视器(锁)的线程操作。所以要使用在同步中,因为只有同步才具有锁。
为什么这些操作线程的方法要定义Object类中呢?
因为这些方法在操作同步中线程时,都必须要标识它们所操作线程只有的锁,只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒。
不可以对不同锁中的线程进行唤醒。
也就是说,等待和唤醒必须是同一个锁。
而锁可以是任意对象,所以可以被任意对象调用的方法定义Object类中。
*/
//仓库容量为1的生产者-消费者问题
public class Thread_6_ProducerConsumerDemo1 {
public static void main(String[] args) {
Res r = new Res();
Input in = new Input(r);
Output out = new Output(r);
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
t1.start();
t2.start();
}
}
class Res{
String name;
String sex;
boolean flag = false; //false代表无数据,true代表有数据
//注意,set方法和out方法的synchronized所指定的锁就是当前对象this,这个下面input和output中的同步锁是同一个功能
public synchronized void set(String name,String sex)
{
//如果像这样吧flag判断和同步动作全部放在这个Res对象里的话,那么下面input和output的一系列同步动作就不需要了
if(flag)
//这里的“this”写不写关系不大,反正是当前对象作为锁进行调用
try{this.wait();}catch(Exception e){}
this.name = name;
this.sex = sex;
flag = true;
this.notify();
}
public synchronized void out()
{
if(!flag)
try{this.wait();}catch(Exception e){}
System.out.println(name+"........"+sex);
flag = false;
this.notify();
}
}
//生产者,不断交替存入两个人的数据
class Input implements Runnable{
private Res r ;
Input(Res r){
this.r = r;
}
public void run(){
int x = 0;
while(true) {
//Input和Output必须使用同一个锁,而传入的Res作为公用的资源,用它作锁是比较合适的(当然其他的也可以,例如Res.class)
synchronized(r) {
//flag为真代表仓库已满,必须等待
if(r.flag)
//当前为input线程持有锁r,由于仓库已满,所以其必须等待,这个等待是依附在锁r上的,
//之后必须在其他线程中执行r.notify()才能唤醒这个input线程
try{r.wait();}catch(Exception e){}
//生产者
if(x==0){
r.set("mike","man");
}else{
r.set("丽丽","女");
}
x = (x+1)%2;
r.flag = true;
//由于input和output使用的锁是同一个东西:r,所以需要通过调用r.notify()进行唤醒
//生产者已经生产完毕,可以唤醒衣服在锁r上的某个线程,注意,如果存在多个生产者和消费者,那么可能随机唤醒任意一个
//如果唤醒的是生产者,那么这个被唤醒的生产者会很不幸的在上几行程序那里再次等待
//如果唤醒的是消费者,那么他进行消费,随后释放锁并叫醒某个线程,有可能是生产者,也有可能某个运气不好的消费者
//所以如果存在多个生产者和多个消费者,最好使用r.notifyAll()
r.notify();
}
}
}
}
//消费者,不断输出现在存储在系统中的人的信息
class Output implements Runnable{
private Res r ;
Output(Res r){
this.r = r;
}
public void run(){
while(true) {
synchronized(r) {
//flag非假代表仓库为空,消费者必须等待
if(!r.flag)
//仓库为空,消费者依附在r上进行等待,并释放锁r,
try{r.wait();}catch(Exception e){}
//消费者
System.out.println(r.name+"...."+r.sex);
r.flag = false;
//消费完成,唤醒依附在r上的某个线程
r.notify();
}
}
}
}
/*
//将同步代码放入Res类之后的化简版本的Input和Output
class Input implements Runnable{
private Res r ;
Input(Res r){
this.r = r;
}
public void run(){
int x = 0;
while(true) {
if(x==0)
r.set("mike","man");
else
r.set("丽丽","女女女女女");
x = (x+1)%2;
}
}
}
class Output implements Runnable{
private Res r ;
Output(Res r){
this.r = r;
}
public void run(){
while(true) {
r.out();
}
}
}
*/