JAVA学习第二十六课(多线程(五))- 多线程间的通信问题
一、线程间的通信
实例代码:
需求是:输入一个姓名和性别后,就输出一个姓名和性别
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 = "BLF"; r.sex = "male"; } else { r.name = "妮妮妮妮"; r.sex = "female"; } x = (++x)%2; } } } } class Output implements Runnable { Resource r; public Output(Resource r) { // TODO Auto-generated constructor stub this.r = r; } public void run() { while(true) { synchronized (r) //输入和输出都是应用同一个锁 { System.out.println(r.name+"..."+r.sex); } } } } public class Main { 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(); } }
上述代码尽管攻克了。多线程处理同一资源问题,可是出现了一个问题就是,打印非常多一个人名和性别后再打印还有一个,无法实现交替输出。原因:输入线程获取运行权后,不会运行一次,输入姓名和性别,输入线程还拥有运行权。一直赋值。等到切换到输出运行权后,输出最后输入的姓名和性别。可是输出线程也不会仅仅输出一次,一直输出,所以出现打印非常多同一姓名和性别的问题
二、线程的等待/唤醒机制:
用一个boolean值推断是否有数据,有就不放。没有就放
输入线程运行时推断是否有数据,有。就释放运行权,再释放运行资格,进入冻结状态。输出线程运行。输出后boolean值置为false
Input: if(flag)//有值 wait();//当前冻结,切换输出线程 flag = true; notify(); Output: if(!flag) wait(); flag = false;//flag置为假。输出完成 notify();//唤醒输入线程
等待/唤醒机制:
方法:
1.wait();//使线程进入冻结状态,CPU释放运行权和运行资格,被wait()的线程会被存储到线程池中 2.notify();//唤醒线程池中的随意的一个线程 3.notifyAll();//唤醒全部线程,使之处于运行状态或暂时堵塞状态,总之,使其具备运行资格
这些方法必须定义在同步中。这些方法是用于操作线程状态的方法,所以必须明白在哪个锁上的线程
(wait()A锁的线程。仅仅能用A锁的notify唤醒A锁的线程。
)
了解:而wait()等方法是定义在Object类中的。这些方法是监视器的方法。而监视器呢。就能够理解为锁,控制哪个锁所属下的线程,而锁又能够是随意的,而全部的类都是继承于Object类的,所以wait()等类是定义在Object里
下述代码就攻克了上述代码的问题,实现了输入输出线程的交替运行
class Resource extends Object { 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) { try { r.wait(); } catch (Exception e) { // TODO: handle exception } } if(x==0) { r.name = "BLF"; r.sex = "male"; } else { r.name = "妮妮妮妮"; r.sex = "female"; } r.flag = true; r.notify(); x = (++x)%2; } } } } class Output implements Runnable { Resource r; public Output(Resource r) { // TODO Auto-generated constructor stub this.r = r; } public void run() { while(true) { synchronized (r) { if(!r.flag) { try { r.wait(); } catch (Exception e) { // TODO: handle exception } } System.out.println(r.name+"..."+r.sex); r.flag = false; r.notify(); } } } } public class Main { 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类中运行,应用同步函数
class Resource extends Object { private String name; private String sex ; private boolean flag = false; public synchronized void set(String name,String sex) { // TODO Auto-generated constructor stub if(flag) { try { this.wait(); } catch (Exception e) { // TODO: handle exception } } this.name = name; this.sex = sex; flag = true; this.notify(); } public synchronized void out() { if(!flag) { try { this.wait(); } catch (Exception e) { // TODO: handle exception } System.out.println(name+" : "+sex); flag = false; this.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("BLF", "male"); } else { r.set("妮妮妮妮", "female"); } x = (++x)%2; } } } class Output implements Runnable { Resource r; public Output(Resource r) { // TODO Auto-generated constructor stub this.r = r; } public void run() { while(true) { r.out(); } } } public class Main { 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(); } }