Java学习个人备忘录之线程间的通信
线程间通讯
多个线程在处理同一资源,但是任务却不同.
class Resource { String name; String sex; } //输入 class Input implements Runnable { Resource r; Input(Resource r) { this.r = r; } public void run() { int i = 0; while(true) { synchronized(r) //保证两个线程用同一个锁 { if (i==0) { r.name = "mike"; r.sex = "nan"; } else { r.name = "丽丽"; r.name = "女女女女女女女女女"; } 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(): 让线程处于冻结状态, 被wait的线程会被存储到线程池中.
2. notify(): 唤醒线程池中一个线程(任意)
3. notifyAll(): 唤醒线程池中的所有线程.
这些方法都必须定义在同步中,
因为这些方法都是用于操做线程状态的方法.
必须要明确到底操做的是哪个锁上的线程.
为什么操做线程的方法wait notify 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 i = 0; while(true) { synchronized(r) //保证两个线程用同一个锁 { if (r.flag) { r.wait(); } if (i==0) { r.name = "mike"; r.sex = "nan"; } else { r.name = "丽丽"; r.name = "女女女女女女女女女"; } 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) { r.wait(); } System.out.println(r.name+"....."+r.sex); r.flag = false; r.notify(); //唤醒对方 } } } } class ResourceDemo2 { 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(); } }
上面代码的优化
class Resource { private String name; //这里要私有化 private String sex; boolean flag = false; public synchronized void set(String name,String sex) //对数据要可控化 { if (this.flag) try{this.wait();}catch(InterruptedException e){} this.name = name; this.sex = sex; flag = true; this.notify(); } public synchronized void out() { if (this.flag) try{this.wait();}catch(InterruptedException e){} 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 i = 0; while(true) { if (i==0) { r.set("mike","nan"); } 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 ResourceDemo3 { 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(); } }
多生产者多消费者问题
class Resource { private String name; private int count = 1; private boolean flag = false; public synchronized void set(String name) { if (flag) try{this.wait();}catch(InterruptedException e){} this.name = name + count; count++; System.out.println(Thread.currentThread().getName()+".....生产者....."+this.name); flag = true; notify(); } public synchronized void out() { if (!flag) try{this.wait();}catch(InterruptedException e){} System.out.println(Thread.currentThread().getName()+".....消费者....."+this.name); flag = false; notify(); } } class Producer implements Runnable { private Resource r; Producer(Resource r) { this.r = r; } public void run() { while (true) { r.set("烤鸭"); } } } class Consumer implements Runnable { private Resource r; Consumer(Resource r) { this.r = r; } public void run() { while(true) { r.out(); } } } class ProducerConsumerDemo { public static void main(String[] args) { Resource r = new Resource(); Producer pro = new Producer(r); Consumer con = new Consumer(r); Thread t0 = new Thread(pro); Thread t1 = new Thread(pro); Thread t2 = new Thread(con); Thread t3 = new Thread(con); t0.start(); t1.start(); t2.start(); t3.start(); } }
但是这样会出现安全隐患, 从这4个线程上看, 一共分了两组, t0和t1一组, t2和t3一组, 当t1 t2 t3 睡眠时, t0出来后再次唤醒t1, 这时t1是不用判断的if条件的,直接向下继续执行. 这样就又进行了"生产烤鸭", 所以出现了安全隐患. 解决办法: 将两个if 换成 while, 这样在t1醒来的时候会继续判断flag是否为真. 但是这样又会出现死锁现象, 因为t1判断flag时, flag为真, 这时t1会再次等待,这时4个线程都进入等待状态---死锁!!
解决办法1
将notify换成notifyAll, 这样就一定会唤醒对方的线程,同时自己方的线程因为while循环出不去.
class Resource { private String name; private int count = 1; private boolean flag = false; public synchronized void set(String name) { while (flag) try{this.wait();}catch(InterruptedException e){} this.name = name + count; count++; System.out.println(Thread.currentThread().getName()+".....生产者....."+this.name); flag = true; notifyAll(); } public synchronized void out() { while (!flag) try{this.wait();}catch(InterruptedException e){} System.out.println(Thread.currentThread().getName()+".....消费者....."+this.name); flag = false; notifyAll(); } } class Producer implements Runnable { private Resource r; Producer(Resource r) { this.r = r; } public void run() { while (true) { r.set("烤鸭"); } } } class Consumer implements Runnable { private Resource r; Consumer(Resource r) { this.r = r; } public void run() { while(true) { r.out(); } } } class ProducerConsumerDemo { public static void main(String[] args) { Resource r = new Resource(); Producer pro = new Producer(r); Consumer con = new Consumer(r); Thread t0 = new Thread(pro); Thread t1 = new Thread(pro); Thread t2 = new Thread(con); Thread t3 = new Thread(con); t0.start(); t1.start(); t2.start(); t3.start(); } }
if判断标记只有一次, 会导致不该运行的线程运行了, 出现了数据错误的情况. while判断标记, 解决了线程获取执行权后, 是否要运行。
notify: 只能唤醒一个线程, 如果本方唤醒了本方, 就没有意义, 而且while判断标记notify会导致死锁. notifyAll解决了, 本方线程一定会唤醒对方线程.
解决办法2:
JDK1.5新特征的解决办法--Lock
可以看出来, 上面的解决方法会造成多次无用的判断, 这会降低效率,可以用这面的方法解决.
Lock l = new ReentrantLock(); void show() { l.lock(); //获取锁 code... l.unlock(); //释放锁 }
jdk1.5以后将同步和锁封装成了对象.
并将操作锁的隐式方法定义到了该对象中,
将隐式动作变成了显示动作.
但是如果执行的代码抛出了异常, 这样代码就会一直持有锁,不释放,所以要如下
Lock l = new ReentrantLock(); void show() { l.lock(); //获取锁 try { code... } finally { l.unlock(); //释放锁 } }
import java.util.concurrent.locks.* class Resource { private String name; private int count = 1; private boolean flag = false; Lock l = new ReentrantLock();//因为Lock是java.util.concurrent.locks包中的类, 所以要先导入包. public void set(String name) //这里的同步就可以去掉了 { l.lock(); //在这里加上锁 try { while (flag) try{this.wait();}catch(InterruptedException e){} this.name = name + count; count++; System.out.println(Thread.currentThread().getName()+".....生产者....."+this.name); flag = true; notifyAll(); } finally { l.unlock(); } } public void out() { l.lock(); try { while (!flag) try{this.wait();}catch(InterruptedException e){} System.out.println(Thread.currentThread().getName()+".....消费者....."+this.name); flag = false; notifyAll(); } finally { l.unlock(); } } } class Producer implements Runnable { private Resource r; Producer(Resource r) { this.r = r; } public void run() { while (true) { r.set("烤鸭"); } } } class Consumer implements Runnable { private Resource r; Consumer(Resource r) { this.r = r; } public void run() { while(true) { r.out(); } } } class ProducerConsumerDemo { public static void main(String[] args) { Resource r = new Resource(); Producer pro = new Producer(r); Consumer con = new Consumer(r); Thread t0 = new Thread(pro); Thread t1 = new Thread(pro); Thread t2 = new Thread(con); Thread t3 = new Thread(con); t0.start(); t1.start(); t2.start(); t3.start(); } }
解决办法3:
JDK1.5新特征的解决办法--Condition
Condition在底层上是这样实现的:
interface Condition
{
await();
signal();
signalAll();
}
所以要这样实现, 如下:
Lock l = new ReectrantLock();
Condition c1 = l.newCondition();
Condition c2 = l.newCondition();
import java.util.concurrent.locks.* class Resource { private String name; private int count = 1; private boolean flag = false; //创建一个锁对象. Lock l = new ReentrantLock();//因为Lock是java.util.concurrent.locks包中的类, 所以要先导入包. //通过已有的锁获取该锁上的监视器对象. Condition con = l.newCondition(); public void set(String name) //这里的同步就可以去掉了 { l.lock(); //在这里加上锁 try { while (flag) // try{this.wait();}catch(InterruptedException e){} //这里用con.await()替换 try{con.await();}catch(InterruptedException e){} this.name = name + count; count++; System.out.println(Thread.currentThread().getName()+".....生产者....."+this.name); flag = true; // notifyAll(); //这里用con.signalAll() 替换 con.signalAll(); } finally { l.unlock(); } } public void out() { l.lock(); try { while (!flag) // try{this.wait();}catch(InterruptedException e){} //这里用con.await()替换 try{con.await();}catch(InterruptedException e){} System.out.println(Thread.currentThread().getName()+".....消费者....."+this.name); flag = false; // notifyAll(); //这里用con.signalAll() 替换 con.signalAll(); } finally { l.unlock(); } } } class Producer implements Runnable { private Resource r; Producer(Resource r) { this.r = r; } public void run() { while (true) { r.set("烤鸭"); } } } class Consumer implements Runnable { private Resource r; Consumer(Resource r) { this.r = r; } public void run() { while(true) { r.out(); } } } class ProducerConsumerDemo2 { public static void main(String[] args) { Resource r = new Resource(); Producer pro = new Producer(r); Consumer con = new Consumer(r); Thread t0 = new Thread(pro); Thread t1 = new Thread(pro); Thread t2 = new Thread(con); Thread t3 = new Thread(con); t0.start(); t1.start(); t2.start(); t3.start(); } }
其实解决办法3和解决办法2没有太大的区别.并没有真的运用了1.5的新特征。
解决办法4
这个解决办法才真正的运用到了1.5的新特征。
import java.util.concurrent.locks.* class Resource { private String name; private int count = 1; private boolean flag = false; //创建一个锁对象. Lock l = new ReentrantLock();//因为Lock是java.util.concurrent.locks包中的类, 所以要先导入包. //通过已有的锁获取该锁上的监视器对象. // Condition con = l.newCondition(); //通过已有的锁获取两组监视器, 一组监视生产者, 一组监视消费者. Condition producer_con = l.newCondition(); Condition consumer_con = l.newCondition(); public void set(String name) //这里的同步就可以去掉了 { l.lock(); //在这里加上锁 try { while (flag) try{producer_con.await();}catch(InterruptedException e){} //这里只让生产者等待 this.name = name + count; count++; System.out.println(Thread.currentThread().getName()+".....生产者....."+this.name); flag = true; consumer_con.signal(); //这里直接唤醒消费者 } finally { l.unlock(); } } public void out() { l.lock(); try { while (!flag) try{consumer_con.await();}catch(InterruptedException e){} //这里只让消费者等待 System.out.println(Thread.currentThread().getName()+".....消费者....."+this.name); flag = false; producer_con.signalAll(); //这里只唤醒生产者 } finally { l.unlock(); } } } class Producer implements Runnable { private Resource r; Producer(Resource r) { this.r = r; } public void run() { while (true) { r.set("烤鸭"); } } } class Consumer implements Runnable { private Resource r; Consumer(Resource r) { this.r = r; } public void run() { while(true) { r.out(); } } } class ProducerConsumerDemo2 { public static void main(String[] args) { Resource r = new Resource(); Producer pro = new Producer(r); Consumer con = new Consumer(r); Thread t0 = new Thread(pro); Thread t1 = new Thread(pro); Thread t2 = new Thread(con); Thread t3 = new Thread(con); t0.start(); t1.start(); t2.start(); t3.start(); } }