多个线程在处理同一个资源,但是线程的任务却不相同,通过一定的手段使各个线程能有效地利用资源,
这种手段即:等待唤醒机制,又称作线程之间的通信
涉及到的方法:wait(),notify()
示例:
两个线程一个输入,一个输出
package demo; public class Resource { public String name; public String sex; }
输入线程:
package demo; public class Input implements Runnable { private Resource r = new Resource(); public void run() { int i = 0; while (true) { if (i % 2 == 0) { r.name = "张三"; r.sex = "男"; } else { r.name = "李四"; r.sex = "女"; } i++; } } }
输出线程:
package demo; public class Output implements Runnable { private Resource r = new Resource(); public void run(){ while (true) { System.out.println(r.name+"..."+r.sex); } } }
测试类:
package demo; public class ThreadDemo { public static void main(String[] args) { Input in = new Input(); Output out = new Output(); Thread tin = new Thread(in); Thread tout = new Thread(out); tin.start(); tout.start(); } }
运行后却发现输出的都是null...null
因为输入线程和输出线程中创建的Resource对象使不同的
解决null问题:
package demo; public class Input implements Runnable { private Resource r; public Input(Resource r){ this.r = r; } public void run() { int i = 0; while (true) { if (i % 2 == 0) { r.name = "张三"; r.sex = "男"; } else { r.name = "李四"; r.sex = "女"; } i++; } } }
package demo; public class Output implements Runnable { private Resource r; public Output(Resource r){ this.r = r; } public void run(){ while (true) { System.out.println(r.name+"..."+r.sex); } } }
package demo; public class ThreadDemo { public static void main(String[] args) { Resource r = new Resource(); Input in = new Input(r); Output out = new Output(r); Thread tin = new Thread(in); Thread tout = new Thread(out); tin.start(); tout.start(); } }
运行后又发现了另一个问题:
输出中含有:张三...女或者李四...男,性别出错
发生原因:
赋值完张三和男后,继续赋值李四和女,这时候还未还得及赋值女,就进入了输出线程,这时候就会输出李四...男
于是想到加上同步:
public void run() { int i = 0; while (true) { synchronized (this) { if (i % 2 == 0) { r.name = "张三"; r.sex = "男"; } else { r.name = "李四"; r.sex = "女"; } i++; } } }
public void run() { while (true) { synchronized (this) { System.out.println(r.name + "..." + r.sex); } } }
然而问题并没有解决:
原因:
这里的同步失去了作用,用到的不是一个锁
解决办法:
使用一个共同的锁即可
public void run() { int i = 0; while (true) { synchronized (r) { if (i % 2 == 0) { r.name = "张三"; r.sex = "男"; } else { r.name = "李四"; r.sex = "女"; } i++; } } }
public void run() { while (true) { synchronized (r) { System.out.println(r.name + "..." + r.sex); } } }
这时候就是正常的输出了
但是还是存在一个问题,我们希望的是张三和李四交错出现,一个张三一个李四,现在依然是随机出现的,大片的张三或李四
解决办法:
先让input线程赋值,然后让output线程输出,并且让输入线程等待,不允许再赋值李四,等待输出张三结束后,再允许李四赋值,依次下去
输入线程也需要同样的方式,输出完后要等待
这时候就需要用到等待唤醒机制:
输入:赋值后,执行方法wait()永远等待
输出:打印后,再输出等待之前,唤醒输入notify(),自己再wait()永远等待
输入:被唤醒后,重新赋值,必须notify()唤醒输出的线程,自己再wait()等待
依次循环下去
代码实现:
package demo; public class Resource { public String name; public String sex; public boolean flag = false; }
package demo; public class Input implements Runnable { private Resource r; public Input(Resource r) { this.r = r; } public void run() { int i = 0; while (true) { synchronized (r) { if (r.flag) { try { r.wait(); } catch (Exception e) { } } if (i % 2 == 0) { r.name = "张三"; r.sex = "男"; } else { r.name = "李四"; r.sex = "女"; } r.flag = true; r.notify(); } i++; } } }
package demo; public class Output implements Runnable { private Resource r; public Output(Resource r) { this.r = r; } public void run() { while (true) { synchronized (r) { if (!r.flag) { try { r.wait(); } catch (Exception e) { } } System.out.println(r.name + "..." + r.sex); r.flag = false; r.notify(); } } } }
package demo; public class ThreadDemo { public static void main(String[] args) { Resource r = new Resource(); Input in = new Input(r); Output out = new Output(r); Thread tin = new Thread(in); Thread tout = new Thread(out); tin.start(); tout.start(); } }
这时候就是张三李四交错输出了
完成