多线程编程之线程间的通信——wait and notify
来源:互联网 发布:ubuntu软件中心打不开 编辑:程序博客网 时间:2024/06/06 02:32
wait/notify基本概念
wait自动释放锁
当执行wait方法后,当前线程进入阻塞队列等待唤醒,唤醒后的线程进入就绪队列,等待cup分配资源。
当执行wait方法后,当前线程自动释放锁,进入阻塞队列。
package waitLetLockGo;public class Service { public void testMethod(Object lock) { try { synchronized (lock) { System.out.println("start wait"); lock.wait(); //Thread.sleep(5000);//若将代码改成休眠,那么就成了同步了。 System.out.println("end wait"); } } catch (InterruptedException e) { e.printStackTrace(); } }}package waitLetLockGo;public class ThreadA extends Thread{ Object lock; public ThreadA(Object lock) { super(); this.lock=lock; } public void run() { Service service=new Service(); service.testMethod(lock); }}package waitLetLockGo;public class ThreadB extends Thread{ Object lock; public ThreadB(Object lock) { super(); this.lock=lock; } public void run() { Service service=new Service(); service.testMethod(lock); }}package waitLetLockGo;//线程A先获得锁,然后线程A进入阻塞队列,然后线程B获得锁,进入阻塞队列。//都没有被唤醒//结论:执行wait会释放锁。public class Run { public static void main(String[] args) { Object lock=new Object(); ThreadA a=new ThreadA(lock); a.start(); ThreadB b=new ThreadB(lock); b.start(); }}
控制台输出:
start waitstart wait
结论:执行wait会释放锁。
notify不释放锁:
package notifyNotLetLockGo;public class Service { public void testMethod(Object lock) { try { synchronized (lock) { System.out.println("begin wait() ThreadName:"+Thread.currentThread().getName()); lock.wait(); System.out.println("end wait() ThreadName:"+Thread.currentThread().getName()); } }catch(InterruptedException e){ e.printStackTrace(); } } public void synNotifyMethod(Object lock) { try { synchronized (lock) { System.out.println("start notify ThreadName:"+Thread.currentThread().getName()+" time:"+System.currentTimeMillis()); lock.notify(); Thread.sleep(5000); System.out.println("end notify ThreadName:"+Thread.currentThread().getName()+" time"+System.currentTimeMillis()); } }catch(InterruptedException e) { e.printStackTrace(); } }}package notifyNotLetLockGo;public class ThreadA extends Thread{ Object lock; public ThreadA(Object lock) { super(); this.lock=lock; } public void run() { Service service=new Service(); service.testMethod(lock); }}package notifyNotLetLockGo;public class NotifyThread extends Thread{ Object lock; public NotifyThread(Object lock) { super(); this.lock=lock; } public void run() { Service service=new Service(); service.synNotifyMethod(lock); }}package notifyNotLetLockGo;public class synNotifyMethodThread extends Thread{ Object lock; public synNotifyMethodThread(Object lock) { super(); this.lock=lock; } public void run() { Service service=new Service(); service.synNotifyMethod(lock); }}package notifyNotLetLockGo;//实验目的:必须执行完notify方法所在的同步代码块后才释放锁。public class Test { public static void main(String[] args) { Object lock=new Object(); ThreadA a=new ThreadA(lock); a.start(); NotifyThread notifyThread=new NotifyThread(lock); notifyThread.start(); synNotifyMethodThread c=new synNotifyMethodThread(lock); c.start(); }}
控制台输出:
begin wait() ThreadName:Thread-0start notify ThreadName:Thread-1 time:1506777344320end notify ThreadName:Thread-1 time1506777349321end wait() ThreadName:Thread-0start notify ThreadName:Thread-2 time:1506777349321end notify ThreadName:Thread-2 time1506777354321
结论:必须执行完notify方法所在的同步synchronized代码块后才释放锁。
就像sleep(arg)方法一样,wait()方法也可以有参数,例如wait(2000),当线程调用后进入等待的状态,如果在2000毫秒内没有其他线程对其唤醒,那么等待的线程就会自动的唤醒。
package waitLong;public class MyRunnable { static private Object lock=new Object(); static private Runnable runnable=new Runnable() { public void run(){ try { synchronized (lock) { System.out.println("begin wait time:"+System.currentTimeMillis()); lock.wait(5000); System.out.println("end wait time:"+System.currentTimeMillis()); } }catch(InterruptedException e) { e.printStackTrace(); } } }; public static void main(String[] args) { Thread t=new Thread(runnable); t.start(); }}
控制台输出:
begin wait time:1507688368045end wait time:1507688373046
上面的例子中,在调用了wait(5000)后,并没有其他线程对其唤醒,但是我们看一下控制台可以发现,过了5s后线程自动退出等待的状态了。
当然,在等待的过程中也可以由其他线程对它唤醒。
package waitLong;//提前唤醒public class runnable { static private Object lock=new Object(); static private Runnable runnable=new Runnable() { public void run(){ try { synchronized (lock) { System.out.println("begin wait time:"+System.currentTimeMillis()); lock.wait(5000); System.out.println("end wait time:"+System.currentTimeMillis()); } }catch(InterruptedException e) { e.printStackTrace(); } } }; static private Runnable runnable2=new Runnable() { public void run() { synchronized (lock) { System.out.println("begin notify time:"+System.currentTimeMillis()); lock.notify(); System.out.println("end notify time:"+System.currentTimeMillis()); } } }; public static void main(String[] args) throws InterruptedException { Thread t=new Thread(runnable); t.start(); Thread.sleep(3000); Thread t2=new Thread(runnable2); t2.start(); } }
控制台输出:
begin wait time:1507688633788begin notify time:1507688636788end notify time:1507688636788end wait time:1507688636788
调用notify()方法一次只随机唤醒一个线程,如果想要唤醒多个线程就需要多次调用这个方法,当然你也可以选择调用notifyAll()(使用方法和notify方法相同)。
除了以上的一些用法,还需要注意的就是:
①避免notify过早,如果没有线程进入等待状态,这个notify通知等于未唤醒任何线程。同时当后续线程需要唤醒的时候却没有线程对它唤醒,容易造成无限等待。
解决方案:避免出现无wait状态下使用notify
②等待的条件发生变化,即:线程A等待取一个字符串,当线程B未完成将一个字符串放入一个队列中时候,线程A陷入等待,当线程B将一个字符串放入队列同时发出notifyAll通知,线程A被唤醒了,但是这个字符串却被同时在等待的线程C给截走了,所以线程A虽然被唤醒了,但它被唤醒的条件是无字符串可取,所以说是wait的条件改变了,伴随着的也就是IndexOutOfBoundsException错误。
解决方案:将wait条件控制语句if改为while。
以下就是一个错误的案例:
(如果修改,只需要将类Subtract中if(ValueObject.list.size()==0)更改为while(ValueObject.list.size()==0))
package waitOld;public class Add { private String lock; public Add(String lock) { super(); this.lock=lock; } public void add() { synchronized (lock) { ValueObject.list.add("anyString"); lock.notifyAll(); } }}package waitOld;public class Subtract { private String lock; public Subtract(String lock) { super(); this.lock=lock; } public void subtract() { try { synchronized (lock) { if(ValueObject.list.size()==0) { System.out.println("wait begain thread name:"+Thread.currentThread().getName()); lock.wait(); System.out.println("wait end thread name:"+Thread.currentThread().getName()); } ValueObject.list.remove(0); System.out.println("list size="+ValueObject.list.size()); } }catch(InterruptedException e) { e.printStackTrace(); } }}package waitOld;public class ThreadAdd extends Thread{ private Add p; public ThreadAdd(Add p) { super(); this.p=p; } public void run() { p.add(); }}package waitOld;public class ThreadSubtract extends Thread{ private Subtract r; public ThreadSubtract(Subtract r) { super(); this.r=r; } public void run() { r.subtract(); } }package waitOld;import java.util.ArrayList;import java.util.List;public class ValueObject { public static List list=new ArrayList();}package waitOld;public class Run { public static void main(String[] args) throws InterruptedException { String lock=new String(""); Add add=new Add(lock); Subtract subtract=new Subtract(lock); ThreadSubtract subtract1thread=new ThreadSubtract(subtract); subtract1thread.setName("subtract1thread"); subtract1thread.start(); ThreadSubtract subtract2thread=new ThreadSubtract(subtract); subtract2thread.setName("subtract2thread"); subtract2thread.start(); Thread.sleep(1000); ThreadAdd addthread=new ThreadAdd(add); addthread.start(); }}
生产者消费者模式
等待/通知最经典的案例就是“生产者消费者模式”了。
一生产一消费:操作值
package producterAndConsumer;//生产者public class P { private String lock; public P(String lock) { super(); this.lock=lock; } public void setValue() { try { synchronized (lock) { if(!ValueObject.value.equals("")) { lock.wait(); } String value=System.currentTimeMillis()+"_"+System.nanoTime(); System.out.println("set 的值是:"+value); ValueObject.value=value; lock.notify(); } }catch(InterruptedException e){ e.printStackTrace(); } }}package producterAndConsumer;//消费者public class C { String lock; public C(String lock) { super(); this.lock=lock; } public void getValue() { try { synchronized (lock) { if(ValueObject.value.equals("")) { lock.wait(); } System.out.println("get的值是:"+ValueObject.value); ValueObject.value=""; lock.notify(); } }catch(InterruptedException e) { e.printStackTrace(); } }}package producterAndConsumer;//生产者线程public class ThreadP extends Thread { private P p; public ThreadP(P p) { super(); this.p=p; } public void run() { while(true) { p.setValue(); } }}package producterAndConsumer;//消费者线程public class ThreadC extends Thread { private C c; public ThreadC(C c) { super(); this.c=c; } public void run() { while(true) { c.getValue(); } }}package producterAndConsumer;//实体类,用于存放数据public class ValueObject { public static String value="";}package producterAndConsumer;//测试代码public class run { public static void main(String[] args) { String lock=new String(""); P p=new P(lock); C c=new C(lock); ThreadP threadP=new ThreadP(p); ThreadC threadC=new ThreadC(c); threadP.start(); threadC.start(); }}
以上是一生产者一消费者,在此基础上可以设计多生产者和多消费者。但是会陷入假死状态,即所有线程都进入等待状态。
为什么会出现这样的情况?因为一个生产者线程通知是随机的,可能唤醒生产者线程(生产检测时候发现value中已经有数据了,进入等待状态),也可能唤醒消费者(我们所期望的情况),但是随着程序不断的进行,所有的线程都会进入waiting(假死)状态。
修改方法:将notify()改为notifyAll(),不仅唤醒生产者线程同时也唤醒消费者线程。
上面的例子工具类使用的是包含一个字符串的对象。接下来的例子试讲生产者将数据放入list对象中:
一生产一消费:操作栈
package producterAndConsumer1;import java.util.ArrayList;import java.util.List;//工具类public class MyStack { private List list=new ArrayList(); synchronized public void push() { try { if(list.size()==1) { this.wait(); } list.add("anyString="+Math.random()); this.notify(); System.out.println("push="+list.size()); }catch(InterruptedException e) { e.printStackTrace(); } } synchronized public String pop() { String returnValue=""; try { if(list.size()==0) { System.out.println("pop操作中的"+Thread.currentThread().getName()+"线程呈wait状态"); this.wait(); } returnValue=""+list.get(0); list.remove(0); this.notify(); System.out.println("pop="+list.size()); }catch(InterruptedException e) { e.printStackTrace(); } return returnValue; }}package producterAndConsumer1;//生产者public class P { private MyStack mystack; public P(MyStack mystack) { super(); this.mystack=mystack; } public void putService() { mystack.push(); }}package producterAndConsumer1;//消费者public class C { private MyStack mystack; public C(MyStack mystack) { super(); this.mystack=mystack; } public void popService() { System.out.println("pop="+mystack.pop()); }}package producterAndConsumer1;//生产者线程public class ThreadP extends Thread{ private P p; public ThreadP(P p) { super(); this.p=p; } public void run() { while(true) { p.putService(); } }}package producterAndConsumer1;//消费者线程public class ThreadC extends Thread{ private C c; public ThreadC(C c) { this.c=c; } public void run() { while(true) { c.popService(); } }}package producterAndConsumer1;//测试方法public class run { public static void main(String[] args) { MyStack mystack=new MyStack(); P p=new P(mystack); C c=new C(mystack); ThreadP threadp=new ThreadP(p); ThreadC threadc=new ThreadC(c); threadp.start(); threadc.start(); }}控制台部分结果:pop=anyString=0.10952348259646205pop操作中的Thread-1线程呈wait状态push=1pop=0pop=anyString=0.3117061801875739pop操作中的Thread-1线程呈wait状态push=1pop=0pop=anyString=0.5919274328346097pop操作中的Thread-1线程呈wait状态push=1pop=0pop=anyString=0.8462987957012925pop操作中的Thread-1线程呈wait状态push=1pop=0pop=anyString=0.1697638369373462pop操作中的Thread-1线程呈wait状态
从以上的基础上改造为:一生产多消费,因为条件的更改(其他线程取走了数据,就是我们上面说过的:wait条件发生变化)导致一些线程发错误,同理我们需要将if条件控制更改为while。
若是改成多生产多消费的话:也会像出现像操作值那样假死,所以也要将notify()方法改为notifyAll()方法。
- 多线程编程之线程间的通信——wait and notify
- Java 多线程之线程间的通信——wait及notify方法
- Java 多线程之线程间的通信——wait及notify方法
- 多线程并发之线程间的通信,notify,wait
- 多线程编程入门(2):线程的通信(wait,notify)
- Java 多线程——— 线程间的通信(wait及notify方法)
- Java 多线程(七) 线程间的通信——wait及notify方法
- Java 多线程(七) 线程间的通信——wait及notify方法
- Java 多线程(七) 线程间的通信——wait及notify方法
- Java 多线程(七) 线程间的通信——wait及notify方法
- Java 多线程 线程间的通信——wait及notify方法
- 多线程总结(五)线程间的通信——wait及notify方法
- Java 多线程 线程间的通信——wait及notify方法
- Java 多线程(七) 线程间的通信——wait及notify方法
- Java 多线程(七) 线程间的通信——wait及notify方法
- Java 多线程(七) 线程间的通信——wait及notify方法
- Java 多线程(七) 线程间的通信——wait及notify方法
- 线程间通信——wait notify
- GET方式缓存清除
- HDU 6026 Apple【高精度问题】
- github+hexo+阿里云搭建个人博客
- 《简爱》:爱与自由
- <C++ Primer_5th>习题_3.5
- 多线程编程之线程间的通信——wait and notify
- 网页上搞出个"HelloWorld".........................
- CSS3背景属性
- <C++ Primer_5th>习题_3.6
- Program[01]-The datetime Library
- ROS机器人操作系统中级教程 1
- mysql学习(一)
- 虚拟内存和物理内存的区别
- 【BigHereo 16】-----DataStrutrue 递归基础