一. 引言
刚才的输出中,是一片相同的连续输出。现在我们希望,每输入一次,输出端就输出一次。
在输入前要先明确一下,资源里面是否有数据。有数据的话要先输出,我们要在资源中多定义个变量,用来标记里面是否有数据。
也就是说,资源里面的数据被输出了。
上来先默认资源里面没有数据。输入拿到执行权后,先判断资源里面是否有数据,如果没有,就往里面输入数据。输入好数据后,flag变量就标记资源里面已经有数据。由于输入线程还持有输入,它还是往里面输入数据,但是输入之前会先检查flag变量,一看里面已经有数据了,输入线程再运行就没有意义了。这时,我们需要输入线程停一下。停多长时间,我们是不确定的,但是有一点可以肯定的是,等输出线程取走了数据才可以醒过来。
输入线程在确定有数据后,要失去执行权和执行资格(cpu不用再执行它了),处于冻结状态。冻结状态我们之前学习了两种方法,一个是sleep,一个是wait,
资源里面没东西,我们就不能输出,没东西flag就是false。为假,我就不需要输出,我就要等,flag为假,!flag=true,进入if语句,执行wait()。
右端输出等待后,就要去唤醒左端的输入线程。flag在置为false以后,在下一次等待之前(可能说的是输出线程),先把输入线程唤醒。同样的,在输入线程输入数据后,flase设为true,也要唤醒输出线程,让它取走数据,就是互相唤醒。
说了这么多,就是为了完成输入一次,输出一次的需求。
现在我们要说的就是多线程通信间,用到的非常重要的机制-等待唤醒机制。
二. 等待唤醒机制
nitify或者notifyAll是为了让线程拥有执行资格,现在准备在原有的程序上加入这些动作,
在输入数据前,要先判断,如果为真就等待。不为真,就开始输入数据,输入完之后,flag就改为true,同时要唤醒输出线程。我们一notify,对方就一定执行吗?不一定,有可能对方还没有等待状态。对方没等待,我们唤醒是没有意义的,但是我们空唤醒是可以存在的。如果这时要有执行权,在输入前又要判断,这时为真了,就等待了。
输出端也是这样的。
如果全部线程都wait了,那就没法操作了,需要notifyAll,
一个程序的一个类里面,我们能写的同步有很多,比如有A同步,B同步,C同步,有三个锁,A锁,B锁,C锁。你在A锁里面被wait了,我拿B锁的notify有用吗?没用。所以,wait,notify是用来操作线程的,说白了就是监视线程状态的,一旦状态发生改变了,它们必须明确改变了那个锁上的线程。因为锁的特点在于,是不是在对多个线程进行同步,换句话说,这四个线程属于哥们这锁的,是到我这来运行的,是到我这来运行的,我这锁里面对线程进行操作的话,其他锁的方法能够对我来进行操作么?这是不行的。所以,大家在使用方法的时候,必须要明确自己的锁。你到底是wait哪个锁上的线程,你如果是wait的是A锁上的线程,你就只能用A锁的notify来唤醒A锁上的线程。
道理明白了,具体体现在哪儿呢?现在看程序,wait和notify处于同一个代码块中,你就应该标识出他们所属的锁,代表r这个锁调用wait方法,线程一进来,r锁的线程被r锁的wait方法改变状态,那你就只能用r锁的notify来唤醒。
这就是必须要明确不同的锁。
另一个线程也是这样,flag是r对象里的属性,和锁没什么关系。
现在我们去API中去看看,wait方法,是否具备这样的东西。
java.lang包里的Thread类,在Thread类的方法介绍中,没有发现了wait方法。
wait方法不是线程类中的方法,wait是用来操作同步中的线程的,它居然不是线程的方法,很奇怪。它居然能用,说明它持有这个方法。它自身没有,找它爹去,
为什么wait方法定义在了object类中?没有定义在Thread类中?
点了object类,里面却有wait方法,
其实wait方法调用的是wait(0)方法,
所谓的监视器,就是这个锁r。因为wait在操作指定监视器上的线程。线程进到r里面来,r要监视线程。因为r是一个所属的区域,这个线程都在r这个锁上运行,这个wait是操作r这个监视器上所装载的方法,我们也称之为监视器方法。
监视器干嘛用的?监视线程用的。哪儿监视线程了?r将单个线程放入同步代码块,同时控制其他线程不进入同步代码块,这就有点监视的意味。
截图中说的等待集就是线程池。放哪个线程池里面?如果程序中有两个线程池,就进入r线程池。(我觉着可能是进入各自所属的,所属怎么确定?)
举例来看,看下面的截图,如果同时存在两个同步代码块,各自的锁为a和r,a中有线程wait,r中也有,但是a中wait的线程应该呆在a所在的线程池中。在这里,一个是a的线程池,另一个是r的线程池,不然怎么区分。
如何区分线程池,按照锁来区分。
这些方法都是操作锁上线程的方法。
现在已经加上wait,notify唤醒机制了,能否实现间隔输入输出么?
查阅wait方法后,发现使用它会出现异常,因此要进行处理。对于异常,可以声明可以捕捉,但是这里只能捕捉,因为run方法没有声明。
sleep方法中也出现了这样的异常,sleep和wait都有着共同的特点。它们都能让线程处于冻结状态,像这样的方法都会引发线程异常,到底怎么引发的,后面会谈论到。
修改程序后,结果没有问题。
本质:不断地在做着标记的切换,在做着互相的等待和唤醒的动作。
package test;
/**
*
* @author 高小硕
*
*/
/*等待/唤醒机制
*
* 涉及的方法:
* 1、wait():让线程处于冻结状态,被wait的线程会被存储到线程池中
* 2.notify():唤醒线程池中一个线程(任意)
* 3.notifyAll():唤醒线程池中的所有线程
*
* 这些方法都必须定义在同步中
* 因为这些方法是用于操作线程的方法
*
* 必须要明确到底操作的是哪个锁上的线程
*
* 为什么操作线程的方法wait notify notifyAll定义在了Object类中
*
* 因为这些方法是监视器的方法,监视器其实就是锁。
* 锁可以是任意的对象,
* 任意的对象调用的方式一定定义在Object类中。
*
* 线程间通讯
* 多个线程在处理同一个资源,但是任务却不同
*/
class Resources{
String name;
String sex;
boolean flag = false;
}
//输入
class Inputs implements Runnable
{
Resources r;
Inputs(Resources r) {
this.r = r;
}
@Override
public void run()
{
int x = 0 ;
while (true)
{ synchronized (r)
{
if(r.flag)
try {
r.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (x==0)
{
r.name="mike";
r.sex="nan";
} else {
r.name="丽丽";
r.sex="女女女女";
}
r.flag = true;
r.notify();
}
x = (x+1)%2;
}
}
}
//输出
class Outputs implements Runnable
{
Resources r;
Outputs(Resources r) {
this.r =r;
}
@Override
public void run() {
while (true) {
synchronized (r) {
if (!r.flag)
try {
r.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(r.name+"..."+r.sex);
r.flag = false;
r.notify();
}
}
}
}
public class ResourceDemo2 {
public static void main(String[] args) {
//资源
Resources r = new Resources();
//创建任务
Inputs in = new Inputs(r);
Outputs out = new Outputs(r);
//创建线程,执行路径
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
//开启线程
t1.start();
t2.start();
}
}
参考自:http://www.cnblogs.com/wsw-bk/p/8047051.html