博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
java thread中的wait()和notify()
阅读量:6623 次
发布时间:2019-06-25

本文共 3970 字,大约阅读时间需要 13 分钟。

关于线程的状态

java thread有五种状态类型

  1. 新建状态(New):新创建了一个线程对象。
  2. 就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
  3. 运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
  4. 阻塞状态(Blocked):塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。
  5. 死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

当我们调用线程类的sleep()、suspend()、yield()、wait()等方法时会导致线程进入阻塞状态。

了解更详细信息可以参考:

关于wait()和notify()

  • wait(): 调用任何对象的wait()方法会让当前线程进入等待,直到另一个线程调用同一个对象的notify()或notifyAll()方法。
  • notify():唤醒因调用这个对象wait()方法而阻塞的线程。

首先,sleep()、suspend()、yield ()等方法都隶属于 Thread 类,但wait()/notify()这一对却直接隶属于Object 类,也就是说,所有对象都拥有这一对方法。初看起来这十分不可思议,但是实际上却是很自然的,因为这一对方法阻塞时要释放占用的锁,而锁是任何对象都具有的,调用对象的 wait() 方法导致线程阻塞,并且该对象上的锁被释放。而调用对象的notify()方法则导致因调用该对象的 wait() 方法而阻塞的线程中随机选择的一个解除阻塞(但要等到获得锁后才真正可执行)。

其次,前面叙述的所有方法都可在任何位置调用,但是这一对方法却必须在 synchronized方法或块中调用,理由也很简单,只有在synchronized 方法或块中当前线程才占有锁,才有锁可以释放。同样的道理,调用这一对方法的对象上的锁必须为当前线程所拥有,这样才有锁可以释放。因此,这一对方法调用必须放置在这样的 synchronized方法或块中,该方法或块的上锁对象就是调用这一对方法的对象。若不满足这一条件,则程序虽然仍能编译,但在运行时会出现IllegalMonitorStateException异常。

最后,关于 wait() 和 notify() 方法再说明三点:

  1. 调用 notify() 方法导致解除阻塞的线程是从因调用该对象的 wait() 方法而阻塞的线程中随机选取的,我们无法预料哪一个线程将会被选择,所以编程时要特别小心,避免因这种不确定性而产生问题
  2. 除了 notify(),还有一个方法 notifyAll() 也可起到类似作用,唯一的区别在于,调用 notifyAll() 方法将把因调用该对象的 wait() 方法而阻塞的所有线程一次性全部解除阻塞。当然,只有获得锁的那一个线程才能进入可执行状态。
  3. wait()和notify()必须成对存在。

举例说明

例子一:主线程等待计数线程计算结束后打印计算结果

先写一个计数线程类CountThread:

public class CountThread extends Thread{    int total;    @Override    public void run(){        synchronized (this){            for(int i=0;i<100;i++){                total=total+1;            }             this.notify();//唤醒被阻塞的线程        }    }}

再写一个测试类作为主线程:

public class TestWait {    public static void main(String[] args) {        CountThread countThread=new CountThread();        countThread.start();        synchronized (countThread){            System.out.println("等到countThread线程计算结束...");            try {                countThread.wait();            } catch (InterruptedException e) {                e.printStackTrace();            }            System.out.println("计算的结果是:"+countThread.total);        }    }}

运行结果:

等到countThread线程计算结束...计算的结果是:100

实例二:生产者、消费者

生产者:

public class Producer extends Thread {    public static final int MAX_BOX_SIZE = 5;    Vector
messageBox = new Vector<>(); @Override public void run() { try { while (true) { putMessage(); } } catch (InterruptedException e) { e.printStackTrace(); } } public synchronized void putMessage() throws InterruptedException { while (messageBox.size() == MAX_BOX_SIZE) { this.wait();//当箱子满后则进入等待 } messageBox.add(new Date().toString()); System.out.println("放入一条消息"+new Date().toString()); this.notify();//放入消息后唤醒被锁住的线程(取消息线程) } public synchronized void getMessage() throws InterruptedException { while (messageBox.size() == 0) { this.wait();//当箱子空后进入等待 } String message = (String) messageBox.firstElement(); messageBox.removeElement(message); System.out.println("取出一条消息"+message); this.notify();//删除消息后唤醒被锁住的线程(放消息线程) }}

消费者:

public class Consumer extends Thread{    Producer producer;    public Consumer(Producer producer){        this.producer=producer;    }    @Override    public void run() {        try {            while (true) {                producer.getMessage();                Thread.sleep(500);            }        } catch (InterruptedException e) {            e.printStackTrace();        }    }    public static void main(String[] args) {        Producer producer=new Producer();        Consumer consumer=new Consumer(producer);        producer.start();        consumer.start();    }}

打印结果:

放入一条消息Thu Jul 07 16:44:41 CST 2016放入一条消息Thu Jul 07 16:44:41 CST 2016放入一条消息Thu Jul 07 16:44:41 CST 2016放入一条消息Thu Jul 07 16:44:41 CST 2016放入一条消息Thu Jul 07 16:44:41 CST 2016取出一条消息Thu Jul 07 16:44:41 CST 2016取出一条消息Thu Jul 07 16:44:41 CST 2016取出一条消息Thu Jul 07 16:44:41 CST 2016

转载地址:http://iixpo.baihongyu.com/

你可能感兴趣的文章
分布式OSSIM系统的控制中心
查看>>
突破极限 解决大硬盘上安装Unix新思路
查看>>
Rpm另类用法加固Linux安全
查看>>
CocoStudio游戏发布后资源加密大致实现思路
查看>>
WPF SL 获取RichTextBox 的内容(string)
查看>>
为什么NTFS删除超过4G大文件或数据库文件后FILE RECORD大小表现为0
查看>>
【iOS-Cocos2d开发之三】CCScene切换的所有特效,以及设置屏幕横竖屏!
查看>>
Spring切入点表达式常用写法
查看>>
微软同步框架入门之五--使用WCF同步远程数据
查看>>
Last-Modified、If-Modified-Since 实现缓存和 OutputCache 的区别
查看>>
漂亮彩色验证码 以及 数学运算表达式形式的验证码
查看>>
理解SQL代理错误日志
查看>>
维护计划作业
查看>>
Multipart Internet Mail Extensions (MIME)
查看>>
C# WinForm控件之Dock顺序调整
查看>>
中控科技 ZK Software的售后服务真像一坨屎,技术人员嚣张
查看>>
关于反模式、设计和复用的一些想法
查看>>
NSPredicate过滤数组数据
查看>>
设置MYSQL允许用IP访问
查看>>
spark 数据预处理 特征标准化 归一化模块
查看>>