java多线程通信方式之一:wait/notify
java多线程之间的通信方式有多种:
1.wait(),notify(),notifyAll()方法;2.join()方法;3.通过volatile共享内存的方式进行线程通信的;4.interrupt()方法中断线程; 5.管道通信。
本文主要学习JAVA多线程中的 wait()方法 与 notify()/notifyAll()方法的用法。
①wait() 与 notify/notifyAll 方法必须在同步代码块中使用
②wait() 与 notify/notifyAll() 的执行过程
③中断 调用wait()方法进入等待队列的 线程
④notify 通知的顺序不能错
⑤多线程中测试某个条件的变化用 if 还是用 while?
①wait() 与 notify/notifyAll 方法必须在同步代码块中使用
wait() 与 notify/notifyAll() 是Object类的方法,在执行两个方法时,要先获得锁。那么怎么获得锁呢?
在这篇:JAVA多线程之Synchronized关键字--对象锁的特点文章中介绍了使用synchronized关键字获得锁。因此,wait() 与 notify/notifyAll() 经常与synchronized搭配使用,即在synchronized修饰的同步代码块或方法里面调用wait() 与 notify/notifyAll()方法。
如果wait() 与 notify/notifyAll()不在同步代码块中使用,那么会报错:IllegalMonitorStateException。
②wait() 与 notify/notifyAll() 的执行过程
由于 wait() 与 notify/notifyAll() 是放在同步代码块中的,因此线程在执行它们时,肯定是进入了临界区中的,即该线程肯定是获得了锁的。
当线程执行wait()时,会把当前的锁释放,然后让出CPU,进入等待状态。
当执行notify/notifyAll方法时,会唤醒一个处于等待该 对象锁 的线程,然后继续往下执行,直到执行完退出对象锁锁住的区域(synchronized修饰的代码块)后再释放锁。
从这里可以看出,notify/notifyAll()执行后,并不立即释放锁,而是要等到执行完临界区中代码后,再释放。故,在实际编程中,我们应该尽量在线程调用notify/notifyAll()后,立即退出临界区。即不要在notify/notifyAll()后面再写一些耗时的代码。示例如下:

 1 public class Service {
 2
 3     public void testMethod(Object lock) {
 4         try {
 5             synchronized (lock) {
 6                 System.out.println("begin wait() ThreadName="
 7                         + Thread.currentThread().getName());
 8                 lock.wait();
 9                 System.out.println("  end wait() ThreadName="
10                         + Thread.currentThread().getName());
11             }
12         } catch (InterruptedException e) {
13             e.printStackTrace();
14         }
15     }
16
17     public void synNotifyMethod(Object lock) {
18         try {
19             synchronized (lock) {
20                 System.out.println("begin notify() ThreadName="
21                         + Thread.currentThread().getName() + " time="
22                         + System.currentTimeMillis());
23                 lock.notify();
24                 Thread.sleep(5000);
25                 System.out.println("  end notify() ThreadName="
26                         + Thread.currentThread().getName() + " time="
27                         + System.currentTimeMillis());
28             }
29         } catch (InterruptedException e) {
30             e.printStackTrace();
31         }
32     }
33 }

在第3行的testMethod()中调用 wait(),在第17行的synNotifyMethod()中调用notify()
从上面的代码可以看出,wait() 与 notify/notifyAll()都是放在同步代码块中才能够执行的。如果在执行wait() 与 notify/notifyAll() 之前没有获得相应的对象锁,就会抛出:java.lang.IllegalMonitorStateException异常。
在第8行,当ThreadA线程执行lock.wait();这条语句时,释放获得的对象锁lock,并放弃CPU,进入等待队列。
当另一个线程执行第23行lock.notify();,会唤醒ThreadA,但是此时它并不立即释放锁,接下来它睡眠了5秒钟(sleep()是不释放锁的,事实上sleep()也可以不在同步代码块中调用),直到第28行,退出synchronized修饰的临界区时,才会把锁释放。这时,ThreadA就有机会获得另一个线程释放的锁,并从等待的地方起(第9行)起开始执行。
接下来是两个线程类,线程类ThreadA调用testMethod()方法执行lock.wait();时被挂起,另一个线程类synNotifyMethodThread调用synNotifyMethod()负责唤醒挂起的线程。代码如下:

 1 public class ThreadA extends Thread {
 2     private Object lock;
 3
 4     public ThreadA(Object lock) {
 5         super();
 6         this.lock = lock;
 7     }
 8
 9     @Override
10     public void run() {
11         Service service = new Service();
12         service.testMethod(lock);
13     }
14 }
15
16 public class SynNotifyMethodThread extends Thread {
17     private Object lock;
18
19     public SynNotifyMethodThread(Object lock) {
20         super();
21         this.lock = lock;
22     }
23
24     @Override
25     public void run() {
26         Service service = new Service();
27         service.synNotifyMethod(lock);
28     }
29 }

再接下来是测试类:

 1 public class Test {
 2
 3     public static void main(String[] args) throws InterruptedException {
 4
 5         Object lock = new Object();
 6
 7         ThreadA a = new ThreadA(lock);
 8         a.start();
 9
10         //NotifyThread notifyThread = new NotifyThread(lock);
11        // notifyThread.start();
12
13         SynNotifyMethodThread c = new SynNotifyMethodThread(lock);
14         c.start();
15     }
16 }

③中断 调用wait()方法进入等待队列的 线程
示例代码如下:

 1 public class Service {
 2
 3     public void testMethod(Object lock) {
 4         try {
 5             synchronized (lock) {
 6                 System.out.println("begin wait()");
 7                 lock.wait();
 8                 System.out.println("  end wait()");
 9             }
10         } catch (InterruptedException e) {
11             e.printStackTrace();
12             System.out.println("出现异常");
13         }
14     }
15 }
16
17 public class ThreadA extends Thread {
18
19     private Object lock;
20
21     public ThreadA(Object lock) {
22         super();
23         this.lock = lock;
24     }
25
26     @Override
27     public void run() {
28         Service service = new Service();
29         service.testMethod(lock);
30     }
31 }

注意,在第23行wait()方法是Object类的对象lock调用的。而下面的interrupt()方法是ThreadA类的对象调用的。在ThreadA里面,将Object的对象作为参数传给了testMethod()方法,ThreadA的run()方法去调用testMethod(),从而wait()使ThreadA的线程暂停了(暂停当前执行wait()的线程)。从这里可以看出一个区别:
Object类中与线程有关的方法:
1)notify/notifyAll
2)wait()/wait(long)
java.lang.Thread中与之相关的方法:
1)interrupt()
2)sleep()/sleep(long)
3)join()/suspend()/resume()....
测试类代码如下:

 1 public class Test {
 2
 3     public static void main(String[] args) {
 4
 5         try {
 6             Object lock = new Object();
 7
 8             ThreadA a = new ThreadA(lock);
 9             a.start();
10
11             Thread.sleep(5000);
12
13             a.interrupt();
14         } catch (InterruptedException e) {
15             e.printStackTrace();
16         }
17     }
18 }

当执行第13行的interrupt()时,处于wait中的线程“立即”被唤醒(一般是立即响应中断请求),并抛出异常。此时,线程也就结束了。
④notify 通知的顺序不能错
假设在线程A中执行wait(),在线程B中执行notify()。但如果线程B先执行了notify()然后结束了,线程A才去执行wait(),那此时,线程A将无法被正常唤醒了(还可以通过③中提到的interrupt()方法以抛出异常的方式唤醒^~^)。
这篇文章: JAVA多线程之线程间的通信方式中的第③点提到了notify通知顺序出错会导致 调用wait()进入等待队列的线程再也无法被唤醒了。
⑤多线程中测试某个条件的变化用 if 还是用 while?
以前一直不明白 当在线程的run()方法中需要测试某个条件时,为什么用while,而不用if???直到看到了这个简单的例子,终于明白了。。。。
这个例子是这样的:
有两个线程从List中删除数据,而只有一个线程向List中添加数据。初始时,List为空,只有往List中添加了数据之后,才能删除List中的数据。添加数据的线程向List添加完数据后,调用notifyAll(),唤醒了两个删除线程,但是它只添加了一个数据,而现在有两个唤醒的删除线程,这时怎么办??
如果用 if 测试List中的数据的个数,则会出现IndexOutofBoundException,越界异常。原因是,List中只有一个数据,第一个删除线程把数据删除后,第二个线程再去执行删除操作时,删除失败,从而抛出 IndexOutofBoundException。
但是如果用while 测试List中数据的个数,则不会出现越界异常!!!神奇。
当wait等待的条件发生变化时,会造成程序的逻辑混乱---即,List中没有数据了,再还是有线程去执行删除数据的操作。因此,需要用while循环来判断条件的变化,而不是用if。
示例如下:Add类,负责添加数据:

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();
        }
    }
}
public class ThreadAdd extends Thread {
    private Add p;
    public ThreadAdd(Add p) {
        super();
        this.p = p;
    }
    @Override
    public void run() {
        p.add();
    }
}

Subtract类,负责删除数据----先要进行条件判断,然后执行wait(),这意味着:wait等待的条件可能发生变化!!!

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) {//将这里的if改成while即可保证不出现越界异常!!!!
                    System.out.println("wait begin ThreadName="
                            + Thread.currentThread().getName());
                    lock.wait();
                    System.out.println("wait   end ThreadName="
                            + Thread.currentThread().getName());
                }
                ValueObject.list.remove(0);
                System.out.println("list size=" + ValueObject.list.size());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class ThreadSubtract extends Thread {
    private Subtract r;
    public ThreadSubtract(Subtract r) {
        super();
        this.r = r;
    }
    @Override
    public void run() {
        r.subtract();
    }
}

封装的List队列:
public class ValueObject {
    public static List list = new ArrayList();
}
测试类:

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.setName("addThread");
        addThread.start();
    }
}

参考:《JAVA多线程核心技术》
原文:http://www.cnblogs.com/hapjin/p/5492645.html
java多线程通信方式之一:wait/notify的更多相关文章
- Java多线程使用wait和notify实现生产者消费者模型
		
Java多线程使用wait和notify这两个关键字的学习,通过实现生成者与消费者来成对研究比较科学. 从两个字的意义来讲就是等待与通知这个简单道理. 现在先模拟一个缓存区存储,是用一个list实现的 ...
 - Java多线程中的wait/notify通信模式
		
前言 最近在看一些JUC下的源码,更加意识到想要学好Java多线程,基础是关键,比如想要学好ReentranLock源码,就得掌握好AQS源码,而AQS源码中又有很多Java多线程经典的一些应用:再比 ...
 - Java 多线程编程之:notify 和 wait 用法
		
wait 和 notify 简介 wait 和 notify 均为 Object 的方法: Object.wait() —— 暂停一个线程 Object.notify() —— 唤醒一个线程 从以上的 ...
 - Java多线程编程——wait()和notify()、notifyAll()
		
1.源码 wait() notify() notifyAll()都是Object类中方法.源码如下所示: public final native void notify(); public final ...
 - java多线程的wait、notify/notifyAll区别
		
1.wait().notify/notifyAll() 方法是Object的本地final方法,无法被重写. 2.wait()使当前线程阻塞,前提是 必须先获得锁,一般配合synchronized ...
 - Java多线程(3):wait()/notify()实例
		
下面是代码实例 public class WaitDemo implements Runnable { private Object lock; public WaitDemo(Object lock ...
 - Java多线程通讯---------wait,notify区别
		
class Res{ public String username; public String sex; } class Out extends Thread{ Res res; public Ou ...
 - JAVA多线程之线程间的通信方式
		
(转发) 收藏 记 周日,北京的天阳光明媚,9月,北京的秋格外肃穆透彻,望望窗外的湛蓝的天,心似透过栏杆,沐浴在这透亮清澈的蓝天里,那朵朵白云如同一朵棉絮,心意畅想....思绪外扬, 鱼和熊掌不可兼得 ...
 - java多线程详解(6)-线程间的通信wait及notify方法
		
Java多线程间的通信 本文提纲 一. 线程的几种状态 二. 线程间的相互作用 三.实例代码分析 一. 线程的几种状态 线程有四种状态,任何一个线程肯定处于这四种状态中的一种:(1). 产生(New) ...
 
随机推荐
- Memcached工作原理及常见问题
			
memcached是怎么工作的? Memcached的神奇来自两阶段哈希(two-stage hash).Memcached就像一个巨大的.存储了很多<key,value>对的哈希表.通过 ...
 - BZOJ2186: [Sdoi2008]沙拉公主的困惑(求[1,N!]与M!互素的个数)(线性筛)
			
2186: [Sdoi2008]沙拉公主的困惑 Time Limit: 10 Sec Memory Limit: 259 MBSubmit: 6103 Solved: 2060[Submit][S ...
 - python list 中元素的统计与排序
			
1. 用count和dict. dict的存储是散乱的, 不方面打印. 2. 用sorted. 注意, 得到的是一个元组list, 而不再是dict. dict_x = {} for item ...
 - 使用rollup 开发专业js library
			
rollup 是一个不错的javascript 模块打包器,一般我们用来构建library 安装 npm install -g rollup 参考集成jquey && shortid ...
 - NTP时间服务器的配置
			
1.NTP简介NTP(Network Time Protocol,网络时间协议)是用来使网络中的计算机,时间同步的一种协议.NTP服务器利用NTP协议来提供时间同步服务. 2 .环境准备主机名 ...
 - 【python】Python框架、库和软件资源大全
			
很多来自世界各地的程序员不求回报的写代码为别人造轮子.贡献代码.开发框架.开放源代码使得分散在世界各地的程序员们都能够贡献他们的代码与创新. Python就是这样一门受到全世界各地开源社区支持的语言. ...
 - sql server获取插入记录后的ID
			
select @@IDENTITY --返回为当前会话的所有作用域中的任何表最后生成的标识值. select IDENT_CURRENT('table_name') --返回为任何会话和任何作用域中的 ...
 - 善待Erlang 代码 -- Xref 实践
			
Xref 是一个交叉引用工具,通过分析定义的函数间的调用关系,用于查找函数. 模块. 应用程序和版本之间的依赖关系. 通俗而言,Xref 可以检查代码中函数的调用关系.比如在 moduleA 中的 f ...
 - wamp ssl配置https
			
主要参考的这个博客 http://blog.csdn.net/sdq4700/article/details/36173665 配置完以后重启wamp , https不能正常访问 apache.log ...
 - 黄聪:Navicat for MySQL的1577错误解决
			
今天尝试使用了Windows下的可视化mysql数据库管理工具Navicat,界面清爽,易操作上手,感觉还不错. 不过当连接上mysql后,无论打开任何一个数据库,都会提示:1577 – Cannot ...