线程间通信:多个线程在处理同一资源,但是任务却不同。下面编写一个输入输出实例:
//资源
class Resource
{
String name;
String sex;
}
//输入
class Input implements Runnable
{
Resource r;
Input(Resource r)
{
this.r=r;
}
public void run()
{
int x=0;
while(true)
{
synchronized(r) //多条代码操作资源中的属性,要用同步代码块。锁可以直接用资源对象
{
if(x==0)
{
r.name="mike";
r.sex="man";
}
else
{
r.name="珠珠";
r.sex="女";
}
}
x=(x+1)%2;
}
}
}
//输出
class Output implements Runnable
{
Resource r;
Output(Resource r)
{
this.r=r;
}
public void run()
{
while(true)
{
synchronized(r) //多个线程操作同一资源,要用同步代码块
{
System.out.println(r.name+"..."+r.sex);
}
}
}
}
class ResourceDemo
{
public static void main(String[] args)
{
//创建资源
Resource r=new Resource();
//创建任务
Input in=new Input(r);
Output out=new Output(r);
//创建线程,执行路径
Thread t1=new Thread(in);
Thread t2=new Thread(out);
//开启线程
t1.start();
t2.start();
}
}
运行结果:
结果可以看到总是先大量输出一个人的信息,再输出另一个人的信息,这是因为输入输出线程一直在不停的运行。
而我们更需要当输入一个人信息时就马上输出他的信息,而不是被覆盖,这就引入了线程间通信的--等待唤醒机制。
等待唤醒机制:
涉及的方法:1.wait() :让线程处于冻结状态,让线程释放CPU的执行权和执行资格,被wait的线程会被存储在线程池中。
2.notify(): 唤醒线程池中的一个线程(任意)。
3.notifyAll():唤醒线程池(等待集)中的所有线程。
注:这些方法都必须定义在同步中,因为这些方法是用于用于操作线程状态的方法。必须要明确到底操作的是哪个锁上的线程。
并且以上三个方法定义在Object类中,这是因为这些方法是监视器的方法,监视器其实就是锁,锁可以是任意对象,任意对象调用的方式一定定义在Object类中。
应用等待唤醒机制修改代码:
//资源
class Resource
{
String name;
String sex;
boolean flag=false; //初始化资源对象没有被赋值
}
//输入
class Input implements Runnable
{
Resource r;
Input(Resource r)
{
this.r=r;
}
public void run()
{
int x=0;
while(true)
{
synchronized(r) //多条代码操作资源中的属性,要用同步代码块。锁可以直接用资源对象
{
if(r.flag) //flag为true即资源对象已被赋值,执行wait()方法
try{r.wait();} //代表r锁在调用wait方法
catch(InterruptedException e){} //wait()方法抛出了异常,此处只能捕捉
if(x==0) //flag为false即对象没有赋值,开始对其赋值
{
r.name="mike";
r.sex="man";
}
else
{
r.name="珠珠";
r.sex="女";
}
r.flag=true; //改变标志位的值,表明资源对象已经被赋值
r.notify(); //唤醒输出线程
}
x=(x+1)%2;
}
}
}
//输出
class Output implements Runnable
{
Resource r;
Output(Resource r)
{
this.r=r;
}
public void run()
{
while(true)
{
synchronized(r) //多个线程操作同一资源,要用同步代码块
{
if(!r.flag)
try{r.wait();} //代表r锁在调用wait方法
catch(InterruptedException e){}
System.out.println(r.name+"..."+r.sex);
r.flag=false; //表明资源对象现在没有值,需要被赋值
r.notify(); //唤醒输入线程
}
}
}
}
class ResourceDemo
{
public static void main(String[] args)
{
//创建资源
Resource r=new Resource();
//创建任务
Input in=new Input(r);
Output out=new Output(r);
//创建线程,执行路径
Thread t1=new Thread(in);
Thread t2=new Thread(out);
//开启线程
t1.start();
t2.start();
}
}
运行结果:
间隔产生两个人物信息的输出,不再发生覆盖。
在本例中,Resource类的属性本应是私有的,但我们却并没有这样定义,这样导致Input类和Output类可以直接访问这些属性,就造成了安全问题。因此我们要对代码进行改写优化。
//资源
class Resource
{
private String name;
private String sex;
private boolean flag=false; //初始化资源对象没有被赋值
public synchronized void set(String name,String sex) //同步函数
{
if(flag)
try{this.wait();} catch(InterruptedException e){} //同步函数的锁是this
this.name=name;
this.sex=sex;
flag=true;
this.notify();
}
public synchronized void out() //打印name和sex的值(同步函数)
{
if(!flag)
try{this.wait();} catch(InterruptedException e){}
System.out.println(name+"..."+sex);
flag=false;
notify();
}
}
//输入
class Input implements Runnable
{
Resource r;
Input(Resource 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
{
Resource r;
Output(Resource r)
{
this.r=r;
}
public void run()
{
while(true)
{
r.out();
}
}
}
class ResourceDemo
{
public static void main(String[] args)
{
//创建资源
Resource r=new Resource();
//创建任务
Input in=new Input(r);
Output out=new Output(r);
//创建线程,执行路径
Thread t1=new Thread(in);
Thread t2=new Thread(out);
//开启线程
t1.start();
t2.start();
}
}
运行结果: