Java线程通讯方法之wait()、nofity() 详解

本文将探讨以下问题:

  1. synchronized 代码块使用
  2. notify()与notifyAll()的区别
  3. Java wait(),notify()如何使用

参考文章:

Java并行(2): Monitor

Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition

Java的wait(), notify()和notifyAll()使用心得

原创文章,欢迎转载!

synchronized 代码块使用

 我们知道当多个线程同时访问一个线程安全方法时我们可以使用synchronized关键字来给方法加锁。当某个线程需

要访问方法时,会先获取访问该方法的锁,当访问完毕再释放锁。当多个线程在等待调用队列中,操作系统根据一

定的调度算法,取出下一个线程来执行方法,完成方法的并行到串行的执行过程。每个对象都拥有一个Monitor,我

们可以将Monitor理解为对象的锁。每个线程访问任意对象时必须要先获取该对象的Monitor 才能访问。当synchro-

nized修饰一个对象时,它控制多线程访问该对象的方法正是通过对象的Monitor实现。请看下面计数代码:

public class SynchronizedImpl implements Runnable{
public static MyInteger num = new MyInteger() ;
public void run() {
// 锁定 num 引用的对象
synchronized (num){
// 对num 的成员变量value自增,步进为1
num.setValue(num.getValue()+1);
System.out.println(Thread.currentThread().getName()+":"+SynchronizedImpl.num.getValue());
}
}
public static void main(String[] args) {
for(int i =0 ; i < 1000 ;i++){
Thread t = new Thread(new SynchronizedImpl());
t.start();
}
}
}
class MyInteger{
int value =0 ;
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
}

 上述代码 num 所引用的对象(ps: 事实上num 并不是一个对象,只是栈中的一个引用,记录的是它所引用的对象的地址,下

文为了叙述方便把num 称为对象) ,同时被多个线程访问。(ps:事实上不会是1000个线程同时访问,同时访问一个对象的

线程数小于等于cpu的核心数)。

从打印的结果来看,虽然线程并不是顺序执行,但是保证每个线程都访问一次num对象,并且对num对象的 value 属性 +1 。

notify() 和notifyAll()

  顾名思义notify()是唤醒一个正在等待的线程,notifyAll()是唤醒所有正在等待的线程。这样的解释 并不会让我们很好理解

二者之间的区别。

notify()

  notify是通知操作系统唤醒一个正在等待的获取对象锁的线程,当有多个等待线程时候, 操作系统会根据一定的调度算法调

度一个线程,当正在占有对象锁的线程释放锁的时候操作系统调度的这个线程就会执行。而而剩下的那些没有被notify的线程

就不会获取执行机会。

notifyAll()

  当有多个等待线程时,所有的等待线程都会被唤醒,他们会根据操作系统的调度顺序依次执行。下面的代码说明二者的区别:

public class NofityTest implements Runnable {
private Object lock ;
private int num ;
public NofityTest(Object lock, int i) {
this.lock = lock ;
num = i ;
} @Override
public void run() {
synchronized(lock){
try {
lock.wait();
System.out.println("--- "+num);
} catch (InterruptedException e){
e.printStackTrace();
}
}
} public static void main(String[] args) {
// 利用obj 的wait 方法实
final Object lock = new Object() ;
new Thread(new NofityTest(lock,1)).start();
new Thread(new NofityTest(lock,2)).start();
try {
// 等待另外两个线程进入wait状态
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock){
// 请将 lock.notify() 改成lock.notifyAll() 再执行观察二者区别 !!
lock.notify();
}
}
}

wait()和notify()

 对象的wait()是让当前线程释放该对象Monitor锁并且进入访问该对象的等待队列,当前线程会进入挂起状态,等待操作系统唤起(notify)

挂起的线程重新获取对该对象的访问锁才能进入运行状态。因为自身已经挂起,所以已经挂起的线程无法唤醒自己,必须通过别的线程

告诉操作系统,再由操作系统唤醒。Monitor是不能被并发访问的(否则Monitor状态会出错,操作系统根据错误的状态调度导致系统错乱),

而wait和nofity 正是改变Monitor的状态(请参考 PV操作) 所以使用wait、notify方法时,必须对对象使用synchronized加锁,只有线程获

取对象的Monitor锁之后才能进行wait、notify操作否则将抛出IllegalMonitorStateException异常。我们来看一段代码:

    public class PrintAB implements Runnable{
private Object lock ;
private char ch ;
public PrintAB(Object lock ,char ch){
this.lock = lock ;
this.ch = ch ;
}
@Override
public void run() {
while(true){
synchronized (lock){
try {
/**
* 第一次执行并不会唤醒任何线程,
* 第二次以及以后就会唤醒另外一个线程获取Monitor锁,因为只有一个线程挂起
* 而notify() 就是唤醒一个线程
*/
lock.notify();
System.out.println(Thread.currentThread().getName()+":"+ch);
/**
* synchronized 代码块执行完毕之后才会交出Monitor锁,别的线程才有执行机会
* wait 执行过之后当前线程就挂起了,然后释放锁,接着已经被操作系统notify
* 的线程获取Monitor 开始执行
*/
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
} public static void main(String[] args) {
// 我们最好将 lock 声明为final ,防止重新赋值后导致synchronized锁定对象发生改变。
final Object lock = new Object();
new Thread(new PrintAB(lock,'A')).start();
new Thread(new PrintAB(lock,'B')).start();
}
}

上述代码实现了交替打印 字符A 和字符B 。当一个线程打印完毕之后自己就会挂起,必须等待另外一个线程打印并将

之唤醒,就实现了交替打印的效果。

Java线程通讯方法之wait()、nofity() 详解的更多相关文章

  1. java线程的五大状态,阻塞状态详解

    一.状态简介 一个线程的生命周期里有五大状态,分别是: 新生 就绪 运行 死亡 运行后可能遇到的阻塞状态 二.相关方法 2.1 新生状态 Thread t = new Thread(); 正如我们前面 ...

  2. Java线程同步的四种方式详解(建议收藏)

    ​ Java线程同步属于Java多线程与并发编程的核心点,需要重点掌握,下面我就来详解Java线程同步的4种主要的实现方式@mikechen 目录 什么是线程同步 线程同步的几种方式 1.使用sync ...

  3. java线程并发控制:ReentrantLock Condition使用详解

    本文摘自:http://outofmemory.cn/java/java.util.concurrent/lock-reentrantlock-condition java的java.util.con ...

  4. java线程中yield(),sleep(),wait()区别详解

    1.sleep() 使当前线程(即调用该方法的线程)暂停执行一段时间,让其他线程有机会继续执行,但它并不释放对象锁.也就是说如果有synchronized同步快,其他线程仍然不能访问共享数据.注意该方 ...

  5. Java中堆内存和栈内存详解2

    Java中堆内存和栈内存详解   Java把内存分成两种,一种叫做栈内存,一种叫做堆内存 在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配.当在一段代码块中定义一个变量时,ja ...

  6. Java多线程编程中Future模式的详解

    Java多线程编程中,常用的多线程设计模式包括:Future模式.Master-Worker模式.Guarded Suspeionsion模式.不变模式和生产者-消费者模式等.这篇文章主要讲述Futu ...

  7. 转 Java虚拟机5:Java垃圾回收(GC)机制详解

    转 Java虚拟机5:Java垃圾回收(GC)机制详解 Java虚拟机5:Java垃圾回收(GC)机制详解 哪些内存需要回收? 哪些内存需要回收是垃圾回收机制第一个要考虑的问题,所谓“要回收的垃圾”无 ...

  8. Java多线程编程中Future模式的详解<转>

    Java多线程编程中,常用的多线程设计模式包括:Future模式.Master-Worker模式.Guarded Suspeionsion模式.不变模式和生产者-消费者模式等.这篇文章主要讲述Futu ...

  9. [ 转载 ] Java开发中的23种设计模式详解(转)

    Java开发中的23种设计模式详解(转)   设计模式(Design Patterns) ——可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类 ...

随机推荐

  1. selenium+chromdriver 动态网页的爬虫

    # 获取加载更多的数据有 2 种方法# 第一种就是直接找数据接口, 点击'加载更多' 在Network看下, 直接找到数据接口 # 第二种方法就是使用selenium+chromdriver # se ...

  2. 问题描述:判断一个整数 n 是否为 2 的幂次方

    一.2的幂次方的基本定义 什么样的数为2的幂次方?例如2^0=1,2^1=2,2^2=4……,符合公式2^n(n>=0)的数称为2的幂次方. 如何判断一个数是否为2的幂次方呢?基本思路:把一个数 ...

  3. python基础学习day4

    列表的初识 why:int bool str str: 存储少量的数据. str:切片还是对其进行任何操作,获取的内容全都是str类型.存储的数据单一. what:list list = [66, ' ...

  4. Linux与unix shell编程指南

    第14章 环境和shell变量 1.使用变量时,尽量用花括号将之括起来,防止shell误解变量值. 2.设置变量时的不同模式 variable-name=value 设置实际值到variable-na ...

  5. Java POI 实现Excel相同数据同一颜色,不同数据颜色交替显示

    目录 1.效果图 2.具体代码实现 excel 读取工具类 excel写入和测试类 1.效果图 2.具体代码实现 excel 读取工具类 package utils; import java.io.F ...

  6. rimraf node_modules 突然不能用了 怀疑是yarn的问题,从环境变量将yarn删掉,能用了

    rimraf node_modules 突然不能用了 怀疑是yarn的问题,从环境变量将yarn删掉,能用了

  7. C# 获取系统所有字体

    获取已安装的所有字体列表 System.Drawing.FontFamily StringBuilder str = ); InstalledFontCollection fonts = new In ...

  8. 关于pytorch在windows上编辑的问题集合

    cmake在windows上自动寻找v140(VS2015)的编译器,现在只有VS2013的IDE,所以要修改编译器 修改掉VS2015的编译器名称,报错提示参数CMAKE_C_COMPILER和CM ...

  9. 【Weiss】【第03章】链表例程

    这种基础例程,如之前所提,会有一个实现和一个简单的测试代码. 链表其实没什么可说的,其实包括后面的栈和队列也没什么可说的,直接放代码吧. 下面这个是测试代码 #include <iostream ...

  10. Vulnhub靶场DC-1 WP

    前言 之前提到过最近在做vlunhub的靶场复现工作,今天开始更新writeup吧.(对着walkthrough一顿乱抄嘻嘻嘻) 关于DC-1(官网翻译来的) 描述 DC-1是一个专门构建的易受攻击的 ...