Java线程_Notify,NotifyAll,Wait方法

2024-06-14 02:08

本文主要是介绍Java线程_Notify,NotifyAll,Wait方法,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

       怎么办,好几天没写博客了,心里感觉不踏实。水一篇吧,水水更健康吐舌头。在看Java线程这本书的电子版,看到第四章notify、wait、notifyAll这几个方法,前面的notify和wait还好,比较简单,就是需要注意的是notify和wait方法必须放在同步代码中。可是为什么要这样呢?原因是如果不将notify和wait放到同步代码中的话,他们之间可能会产生竞态条件。现设有两个线程,如果不将notify和wait放在同步代码中可能发生如下情况:

       一、第一个线程检查条件,确定需要等待。
       二、第二个线程设定条件。
       三、第二个线程调用notify()方法,本次调用因为没有其他的等待线程而直接返回。
       四、第一个线程调用wait()。

       附上一段关于notify和wait的Android代码:

package com.wly.testwait_notify;import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.Button;public class MainActivity extends Activity {int count = 0;Button b;CountThread countThread;Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);b.setText(msg.arg1 + "");}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);b = new Button(this);b.setText("0");this.setContentView(b);countThread = new CountThread();countThread.start();b.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if(countThread.isAlive()) {countThread.wakeup();}}});}class CountThread extends Thread {@Overridepublic void run() {super.run();while(count < 20) {sendMsg();try {sleep(500);} catch (InterruptedException e) {e.printStackTrace();}}}//发送通知,并进入等待状态,注意wait()只能位于synchronized代码块中public synchronized void sendMsg() {Message msg = new Message();msg.arg1 = count ++;handler.sendMessage(msg);if(count % 5 == 0) {try {System.out.println("--等待10秒--");wait(10000);} catch (InterruptedException e) {e.printStackTrace();}}}/*** 唤醒线程,注意notify()只能位于synchronized代码块中*/public synchronized void wakeup() {notify();}}
}

      说出来有点好笑的是,笔者以前想在Android中用wait()结果因为没有放到同步代码块中,导致一运行就报错,还以为Android中不能用wait呢!咳咳,,不要笑啦,进入今天正题NotifyAll。可能是最近办公室比较吵得原因吧(抢票的抢票,聊天的聊天),使得笔者静不下心来,于是一个notifyAll盯着看了好久才看懂。

      还是一样分析,为什么要有notifyAll,他和notify有什么区别吗?notifyAll用来唤醒一个对象上的多个等待线程。就是说首先这个对象有多个线程等待着使用该对象,其次就是要唤醒所有的线程,让他们去竞争对象上的锁。这里需要注意一下的是:虽然所有该对象上的等待线程都被唤醒了,但他们还需要获得对象上的锁才能向下运行。于是当调用notifyAll后还是只有那个获取到了锁的线程才能继续往下运行。好了,一段notifyAll的测试代码:

package com.wly.javathread.chap4;/*** 测试notifyAll方法* * @author wly* */
public class TestNotifyAll implements Runnable {static ResourcePool mPool;public static void main(String[] args) {ResourcePool pool = new ResourcePool();System.out.println("总资源数:" + pool.resource_max);TestNotifyAll tn_1 = new TestNotifyAll(pool);TestNotifyAll tn_2 = new TestNotifyAll(pool);TestNotifyAll tn_3 = new TestNotifyAll(pool);TestNotifyAll tn_4 = new TestNotifyAll(pool);new Thread(tn_1).start();new Thread(tn_2).start();new Thread(tn_3).start();new Thread(tn_4).start();}@Overridepublic void run() {while(true) {mPool.getResource(2);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}mPool.freeResource(1);}}public TestNotifyAll(ResourcePool pool) {this.mPool = pool;}static class ResourcePool {int resource_max = 10; //资源最大数量int resource_count = 0; //当前占用资源数量/*** 获取资源* @param num*/public synchronized void getResource(int num) {if((resource_count + num) <= resource_max) { //有可用资源,领取使用resource_count += num;System.out.println("线程" + Thread.currentThread().getId() + "拿到2个资源");System.out.println("剩余资源数:" + (resource_max -resource_count));} else { //无可用资源,等try {System.out.println("资源数量不够,线程" + Thread.currentThread().getId() + ":进入等待状态");wait();} catch (InterruptedException e) {e.printStackTrace();}}}/*** 释放资源* @param num*/public synchronized void freeResource(int num) {if(num <= (resource_count)) {resource_count -= num;System.out.println("释放" + num + "个资源,当前剩余资源" + (resource_max-resource_count));notifyAll();} }}
}

        运行结果如下:

总资源数:10
线程9拿到2个资源
剩余资源数:8
线程11拿到2个资源
剩余资源数:6
线程8拿到2个资源
剩余资源数:4
线程10拿到2个资源
剩余资源数:2
释放1个资源,当前剩余资源3
线程9拿到2个资源
剩余资源数:1
释放1个资源,当前剩余资源2
线程11拿到2个资源
剩余资源数:0
释放1个资源,当前剩余资源1
资源数量不够,线程8:进入等待状态
释放1个资源,当前剩余资源2
线程10拿到2个资源
剩余资源数:0
释放1个资源,当前剩余资源1
资源数量不够,线程9:进入等待状态
释放1个资源,当前剩余资源2
线程11拿到2个资源
剩余资源数:0
释放1个资源,当前剩余资源1
资源数量不够,线程8:进入等待状态
释放1个资源,当前剩余资源2
线程10拿到2个资源
剩余资源数:0
释放1个资源,当前剩余资源1
资源数量不够,线程11:进入等待状态
释放1个资源,当前剩余资源2
线程9拿到2个资源
剩余资源数:0
释放1个资源,当前剩余资源1
资源数量不够,线程8:进入等待状态
释放1个资源,当前剩余资源2
线程10拿到2个资源
剩余资源数:0

       可能运行结果需要仔细看才能看出流程,主要就是释放资源时通知所有等待线程,若此时某个等待线程抢到了锁会进行资源请求,或者进入等待状态(资源不够时)。

      O啦~~~

      转载请保留出处:http://blog.csdn.net/u011638883/article/details/18322537

      谢谢!!

这篇关于Java线程_Notify,NotifyAll,Wait方法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1059079

相关文章

Spring事务传播机制最佳实践

《Spring事务传播机制最佳实践》Spring的事务传播机制为我们提供了优雅的解决方案,本文将带您深入理解这一机制,掌握不同场景下的最佳实践,感兴趣的朋友一起看看吧... 目录1. 什么是事务传播行为2. Spring支持的七种事务传播行为2.1 REQUIRED(默认)2.2 SUPPORTS2

C# 比较两个list 之间元素差异的常用方法

《C#比较两个list之间元素差异的常用方法》:本文主要介绍C#比较两个list之间元素差异,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录1. 使用Except方法2. 使用Except的逆操作3. 使用LINQ的Join,GroupJoin

怎样通过分析GC日志来定位Java进程的内存问题

《怎样通过分析GC日志来定位Java进程的内存问题》:本文主要介绍怎样通过分析GC日志来定位Java进程的内存问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、GC 日志基础配置1. 启用详细 GC 日志2. 不同收集器的日志格式二、关键指标与分析维度1.

Java进程异常故障定位及排查过程

《Java进程异常故障定位及排查过程》:本文主要介绍Java进程异常故障定位及排查过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、故障发现与初步判断1. 监控系统告警2. 日志初步分析二、核心排查工具与步骤1. 进程状态检查2. CPU 飙升问题3. 内存

java中新生代和老生代的关系说明

《java中新生代和老生代的关系说明》:本文主要介绍java中新生代和老生代的关系说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、内存区域划分新生代老年代二、对象生命周期与晋升流程三、新生代与老年代的协作机制1. 跨代引用处理2. 动态年龄判定3. 空间分

MySQL查询JSON数组字段包含特定字符串的方法

《MySQL查询JSON数组字段包含特定字符串的方法》在MySQL数据库中,当某个字段存储的是JSON数组,需要查询数组中包含特定字符串的记录时传统的LIKE语句无法直接使用,下面小编就为大家介绍两种... 目录问题背景解决方案对比1. 精确匹配方案(推荐)2. 模糊匹配方案参数化查询示例使用场景建议性能优

Java设计模式---迭代器模式(Iterator)解读

《Java设计模式---迭代器模式(Iterator)解读》:本文主要介绍Java设计模式---迭代器模式(Iterator),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录1、迭代器(Iterator)1.1、结构1.2、常用方法1.3、本质1、解耦集合与遍历逻辑2、统一

Java内存分配与JVM参数详解(推荐)

《Java内存分配与JVM参数详解(推荐)》本文详解JVM内存结构与参数调整,涵盖堆分代、元空间、GC选择及优化策略,帮助开发者提升性能、避免内存泄漏,本文给大家介绍Java内存分配与JVM参数详解,... 目录引言JVM内存结构JVM参数概述堆内存分配年轻代与老年代调整堆内存大小调整年轻代与老年代比例元空

深度解析Java DTO(最新推荐)

《深度解析JavaDTO(最新推荐)》DTO(DataTransferObject)是一种用于在不同层(如Controller层、Service层)之间传输数据的对象设计模式,其核心目的是封装数据,... 目录一、什么是DTO?DTO的核心特点:二、为什么需要DTO?(对比Entity)三、实际应用场景解析

Java 线程安全与 volatile与单例模式问题及解决方案

《Java线程安全与volatile与单例模式问题及解决方案》文章主要讲解线程安全问题的五个成因(调度随机、变量修改、非原子操作、内存可见性、指令重排序)及解决方案,强调使用volatile关键字... 目录什么是线程安全线程安全问题的产生与解决方案线程的调度是随机的多个线程对同一个变量进行修改线程的修改操