有新理解持续更新

轮询

线程本身是操作系统中独立的个体,但是线程与线程之间不是独立的个体,因为它们彼此之间要相互通信和协作。

想像一个场景,A线程做int型变量i的累加操作,B线程等待i到了10000就打印出i,怎么处理?一个办法就是,B线程while(i == 10000),这样两个线程之间就有了通信,B线程不断通过轮训来检测i == 10000这个条件。

这样可以实现我们的需求,但是也带来了问题:CPU把资源浪费了B线程的轮询操作上,因为while操作并不释放CPU资源,导致了CPU会一直在这个线程中做判断操作。如果可以把这些轮询的时间释放出来,给别的线程用,就好了。

wait/notify

在Object对象中有三个方法wait()、notify()、notifyAll(),既然是Object中的方法,那每个对象自然都是有的。如果不接触多线程的话,这两个方法是不太常见的。下面看一下前两个方法:

1、wait()

wait()的作用是使当前执行代码的线程进行等待,将当前线程置入"预执行队列"中,并且wait()所在的代码处停止执行,直到接到通知或被中断。在调用wait()之前,线程必须获得该对象的锁,因此只能在同步方法/同步代码块中调用wait()方法

2、notify()

notify()的作用是,如果有多个线程等待,那么线程规划器随机挑选出一个wait的线程,对其发出通知notify(),并使它等待获取该对象的对象锁。注意"等待获取该对象的对象锁",这意味着,即使收到了通知,wait的线程也不会马上获取对象锁,必须等待notify()方法的线程释放锁才可以。和wait()一样,notify()也要在同步方法/同步代码块中调用

总结起来就是,wait()使线程停止运行,notify()使停止运行的线程继续运行

wait()/notify()使用示例

看一段代码:

public class MyThread30_0 extends Thread
{
private Object lock; public MyThread30_0(Object lock)
{
this.lock = lock;
} public void run()
{
try
{
synchronized (lock)
{
System.out.println("开始------wait time = " + System.currentTimeMillis());
lock.wait();
System.out.println("结束------wait time = " + System.currentTimeMillis());
}
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
public class MyThread30_1 extends Thread
{
private Object lock; public MyThread30_1(Object lock)
{
this.lock = lock;
} public void run()
{
synchronized (lock)
{
System.out.println("开始------notify time = " + System.currentTimeMillis());
lock.notify();
System.out.println("结束------notify time = " + System.currentTimeMillis());
}
}
}

写个main函数,同样的Thread.sleep(3000)也是为了保证mt0先运行,这样才能看到wait()和notify()的效果:

public static void main(String[] args) throws Exception
{
Object lock = new Object();
MyThread30_0 mt0 = new MyThread30_0(lock);
mt0.start();
Thread.sleep(3000);
MyThread30_1 mt1 = new MyThread30_1(lock);
mt1.start();
}

看一下运行结果:

开始------wait time = 1443931599021
开始------notify time = 1443931602024
结束------notify time = 1443931602024
结束------wait time = 1443931602024

第一行和第二行之间的time减一下很明显就是3s,说明wait()之后代码一直暂停,notify()之后代码才开始运行。

wait()方法可以使调用该线程的方法释放共享资源的锁,然后从运行状态退出,进入等待队列,直到再次被唤醒。

notify()方法可以随机唤醒等待队列中等待同一共享资源的一个线程,并使得该线程退出等待状态,进入可运行状态

notifyAll()方法可以使所有正在等待队列中等待同一共享资源的全部线程从等待状态退出,进入可运行状态

最后,如果wait()方法和notify()/notifyAll()方法不在同步方法/同步代码块中被调用,那么虚拟机会抛出java.lang.IllegalMonitorStateException,注意一下。

wait()释放锁以及notify()不释放锁

多线程的学习中,任何地方都要关注"锁",wait()和notify()也是这样。wait()方法是释放锁的,写一个例子来证明一下:

public class ThreadDomain31
{
public void testMethod(Object lock)
{
try
{
synchronized (lock)
{
System.out.println(Thread.currentThread().getName() + " Begin wait()");
lock.wait();
System.out.println(Thread.currentThread().getName() + " End wait");
}
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
public class MyThread31 extends Thread
{
private Object lock; public MyThread31(Object lock)
{
this.lock = lock;
} public void run()
{
ThreadDomain31 td = new ThreadDomain31();
td.testMethod(lock);
}
}

main函数调用一下:

public static void main(String[] args)
{
Object lock = new Object();
MyThread31 mt0 = new MyThread31(lock);
MyThread31 mt1 = new MyThread31(lock);
mt0.start();
mt1.start();
}

看一下运行结果:

Thread-0 Begin wait()
Thread-1 Begin wait()

如果wait()方法不释放锁,那么Thread-1根本不会进入同步代码块打印的,所以,证明完毕。

接下来证明一下notify()方法不释放锁的结论:

public class ThreadDomain32
{
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("Begin notify(), ThreadName = " + Thread.currentThread().getName());
lock.notify();
Thread.sleep(5000);
System.out.println("End notify(), ThreadName = " + Thread.currentThread().getName());
}
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}

写两个线程分别调用2个方法:

public class MyThread32_0 extends Thread
{
private Object lock; public MyThread32_0(Object lock)
{
this.lock = lock;
} public void run()
{
ThreadDomain32 td = new ThreadDomain32();
td.testMethod(lock);
}
}
public class MyThread32_1 extends Thread
{
private Object lock; public MyThread32_1(Object lock)
{
this.lock = lock;
} public void run()
{
ThreadDomain32 td = new ThreadDomain32();
td.synNotifyMethod(lock);
}
}

写个main函数调用一下:

public static void main(String[] args) throws Exception
{
Object lock = new Object();
MyThread32_0 mt0 = new MyThread32_0(lock);
mt0.start();
MyThread32_1 mt1 = new MyThread32_1(lock);
mt1.start();
MyThread32_1 mt2 = new MyThread32_1(lock);
mt2.start();
}

看一下运行结果:

Begin wait(), ThreadName = Thread-0
Begin notify(), ThreadName = Thread-1
End notify(), ThreadName = Thread-1
Begin notify(), ThreadName = Thread-2
End notify(), ThreadName = Thread-2
End wait(), ThreadName = Thread-0

如果notify()方法释放锁,那么在Thread-1调用notify()方法后Thread.sleep(5000)必定应该有其他线程可以进入同步代码块了,但是实际上没有,必须等到Thread-1把代码执行完。所以,证明完毕。

interrupt()打断wait()

之前有说过,interrupt()方法的作用不是中断线程,而是在线程阻塞的时候给线程一个中断标识,表示该线程中断。wait()就是"阻塞的一种场景",看一下用interrupt()打断wait()的例子:

public class ThreadDomain33
{
public void testMethod(Object lock)
{
try
{
synchronized (lock)
{
System.out.println("Begin wait()");
lock.wait();
System.out.println("End wait()");
}
}
catch (InterruptedException e)
{
System.out.println("wait()被interrupt()打断了!");
e.printStackTrace();
}
}
}
public class MyThread33 extends Thread
{
private Object lock; public MyThread33(Object lock)
{
this.lock = lock;
} public void run()
{
ThreadDomain33 td = new ThreadDomain33();
td.testMethod(lock);
}
}
public static void main(String[] args) throws Exception
{
Object lock = new Object();
MyThread33 mt = new MyThread33(lock);
mt.start();
Thread.sleep(5000);
mt.interrupt();
}

看一下运行结果:

Begin wait()
wait()被interrupt()打断了!
java.lang.InterruptedException
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:485)
at com.xrq.example.e33.ThreadDomain33.testMethod(ThreadDomain33.java:12)
at com.xrq.example.e33.MyThread33.run(MyThread33.java:15)

notifyAll()唤醒所有线程

利用Object对象的notifyAll()方法可以唤醒处于同一监视器下的所有处于wait的线程,举个例子证明一下:

public class ThreadDomain34
{
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();
}
}
}

写两个线程,一个调用testMethod(Object lock)的线程,一个notifyAll()线程:

public class MyThread34_0 extends Thread
{
private Object lock; public MyThread34_0(Object lock)
{
this.lock = lock;
} public void run()
{
ThreadDomain34 td = new ThreadDomain34();
td.testMethod(lock);
}
}
public class MyThread34_1 extends Thread
{
private Object lock; public MyThread34_1(Object lock)
{
this.lock = lock;
} public void run()
{
synchronized (lock)
{
lock.notifyAll();
}
}
}

main函数开三个wait线程,用一个notifyAll的线程去唤醒:

public static void main(String[] args) throws Exception
{
Object lock = new Object();
MyThread34_0 mt0 = new MyThread34_0(lock);
MyThread34_0 mt1 = new MyThread34_0(lock);
MyThread34_0 mt2 = new MyThread34_0(lock);
mt0.start();
mt1.start();
mt2.start();
Thread.sleep(1000);
MyThread34_1 mt3 = new MyThread34_1(lock);
mt3.start();
}

看一下运行结果:

Begin wait(), ThreadName = Thread-0
Begin wait(), ThreadName = Thread-2
Begin wait(), ThreadName = Thread-1
End wait(), ThreadName = Thread-1
End wait(), ThreadName = Thread-2
End wait(), ThreadName = Thread-0

当然,唤醒的顺序不重要,因为notifyAll()把处于同一资源下wait的线程全部唤醒,至于唤醒的顺序,就和线程启动的顺序一样,是虚拟机随机的。

注:本文转自https://www.cnblogs.com/xrq730/p/4853932.html--五月仓颉博客。

wait()、notify()、notifyAll()(三)的更多相关文章

  1. java 多线程之wait(),notify,notifyAll(),yield()

    wait(),notify(),notifyAll()不属于Thread类,而是属于Object基础类,也就是说每个对像都有wait(),notify(),notifyAll()的功能.因为都个对像都 ...

  2. java中的wait(),notify(),notifyAll(),synchronized方法

    wait(),notify(),notifyAll()三个方法不是Thread的方法,而是Object的方法.意味着所有对象都有这三个方法,因为每个对象都有锁,所以自然也都有操作锁的方法了.这三个方法 ...

  3. Java多线程之wait(),notify(),notifyAll()

    在多线程的情况下,因为同一进程的多个线程共享同一片存储空间,在带来方便的同一时候,也带来了訪问冲突这个严重的问题.Java语言提供了专门机制以解决这样的冲突,有效避免了同一个数据对象被多个线程同一时候 ...

  4. Java多线程的wait(),notify(),notifyAll()

    在多线程的情况下.因为多个线程与存储空间共享相同的过程,同时带来的便利.它也带来了访问冲突这个严重的问题. Java语言提供了一种特殊的机制来解决这类冲突,避免同一数据对象由多个线程在同一时间访问. ...

  5. java 为什么wait(),notify(),notifyAll()必须在同步方法/代码块中调用?

    在Java中,所有对象都能够被作为"监视器monitor"——指一个拥有一个独占锁,一个入口队列和一个等待队列的实体entity.所有对象的非同步方法都能够在任意时刻被任意线程调用 ...

  6. 多线程学习-基础(六)分析wait()-notify()-notifyAll()

    一.理解wait()-notify()-notifyAll()obj.wait()与obj.notify()必须要与synchronized(Obj)一起使用,也就是wait,notify是针对已经获 ...

  7. Java Object对象中的wait,notify,notifyAll的理解

    wait,notify,notifyAll 是定义在Object类的实例方法,用于控制线程状态,在线程协作时,大家都会用到notify()或者notifyAll()方法,其中wait与notify是j ...

  8. Java多线程8:wait()和notify()/notifyAll()

    轮询 线程本身是操作系统中独立的个体,但是线程与线程之间不是独立的个体,因为它们彼此之间要相互通信和协作. 想像一个场景,A线程做int型变量i的累加操作,B线程等待i到了10000就打印出i,怎么处 ...

  9. 使用Object的wait,notify,notifyAll做线程调度

    我们知道java中的所有类的祖先都是Object,Object类有四个个方法wait(),wait(long timeout),notify(),notifyAll(),这四个方法可以用来做线程的调度 ...

  10. 【java线程系列】java线程系列之线程间的交互wait()/notify()/notifyAll()及生产者与消费者模型

    关于线程,博主写过java线程详解基本上把java线程的基础知识都讲解到位了,但是那还远远不够,多线程的存在就是为了让多个线程去协作来完成某一具体任务,比如生产者与消费者模型,因此了解线程间的协作是非 ...

随机推荐

  1. 使用regulator_get时的一个小注意事项

    Linux kernel 使用 regulator 框架来管理电源,比如 PMIC 芯片上常见的LDO.使用 regulator 的常规流程如以下代码所示: void set_vbus_voltage ...

  2. 性能分析之CPU分析-从CPU调用高到具体代码行(JAVA)

      通常情况下,性能报告中只说CPU使用率高的时候,并不能帮助定位问题.因为CPU高会有多种不同的情况.CPU有五种状态(us sy id wa st), 在vmstat中能显示出来,这个想必很多人都 ...

  3. 【HTML】同页面锚点跳转

    跳转: <a href="#maodian001">去吧!</a> 锚点: <a id="maodian001"></ ...

  4. apache jmeter下载与安装

    JMeter是Apache软件基金会的产品,用于对静态的和动态的资源性能进行测试.jmeter可以运行在多个平台上,如Windows和Linux,本文讲的是在Windows安装jmeter. 工具/原 ...

  5. 深度解密:Java与线程的关系

    并发不一定要依赖多线程(如PHP的多进程并发),但在Java中谈论并发,大多数都与线程脱不开关系. 线程的实现 线程是CPU调度的基本单位,Thread类与大部分的Java API有显著的差别,它的所 ...

  6. 理解Spring:AOP的原理及手动实现

    引入 到目前为止,我们已经完成了简易的IOC和DI的功能,虽然相比如Spring来说肯定是非常简陋的,但是毕竟我们是为了理解原理的,也没必要一定要做一个和Spring一样的东西.到了现在并不能让我们松 ...

  7. Oracle冷备

    概念:一致性的备份,也就是在数据库一致性关闭后做的备份,一般用:shutdown immediate方式关闭. 步骤:1.查看三大核心文件所在位置:数据文件,控制文件,日志文件 数据文件:select ...

  8. 液晶显示系列(2)之黑色背景的PPT更省电环保吗?常黑与常白型LCD

    原文地址点击这里: 数年前听过一个培训师讲课,他的电脑课件PPT背景颜色是黑色的?美其名曰:黑色省电环保!当时讲台下听课的那些菜鸟们(也包括区区在下)深以为然,不由得心中竖起大拇指:这老师有水平,境界 ...

  9. Kubernetes ConfigMap详解,多种方式创建、多种方式使用

    我最新最全的文章都在南瓜慢说 www.pkslow.com,欢迎大家来喝茶! 1 简介 配置是程序绕不开的话题,在Kubernetes中使用ConfigMap来配置,它本质其实就是键值对.本文讲解如何 ...

  10. 【进阶之路】深入理解Java虚拟机的类加载机制(长文)

    我们在参加面试的时候,经常被问到一些关于类加载机制的问题,也都会在面试之前准备的时候背好答案,但是我们是否有去深入了解什么是类加载机制呢?这段时间因为一些事情在家看了些书,这次就和大家分享一些关于Ja ...