notify() 和 notifyAll()都是唤醒其他正在等待同一个对象锁的线程。

下面是我遇到的一个问题,记下来,免得忘了。

直接上代码,有错误的代码:

代码描述:有一个Caculate类,类中又一个成员变量 j,现在有多个线程对这个变量进行操作。一个增加操作、一个减少操作。增加操作:当 j = 0 时,j++ 。减少操作:当 j = 1 时,j-- 。这两个操作分别对应这 add()方法 和sub()方法,都使用synchronized关键字。

可以直接复制拿来运行一下

package com.zcd2;

public class ThreadTest1
{
public static void main(String[] args)
{
Caculate caculate = new Caculate(); //使用多个线程对实例caculate进行增加操作。
for(int i = 0; i < 10; i++)
{
Thread1 t = new Thread1(caculate);
t.start();
} //使用多个线程对实例caculate进行减少操作。
for(int i = 0; i < 2; i++)
{
Thread2 t = new Thread2(caculate);
t.start();
}
}
} //Thread1线程进行增加操作
class Thread1 extends Thread
{
private Caculate caculate; public Thread1()
{ } public Thread1(Caculate caculate)
{
this.caculate = caculate;
} @Override
public void run()
{
int i = 0; //死循环,手动停止
while(true)
{
try
{
caculate.add();
} catch (InterruptedException e)
{
e.printStackTrace();
}
i++;
System.out.println("加线程执行第 " + i + " 次");
}
}
} //Thread2进行减少操作。
class Thread2 extends Thread
{
private Caculate caculate; public Thread2()
{
} public Thread2(Caculate caculate)
{
this.caculate = caculate;
} @Override
public void run()
{
int i = 0; //死循环,手动停止
while(true)
{
try
{
caculate.sub();
} catch (InterruptedException e)
{
e.printStackTrace();
}
i++;
System.out.println("减线程执行第 " + i + " 次");
}
}
} //
class Caculate
{
private int j = 0; //增加操作
public synchronized void add() throws InterruptedException
{
     //当 j = 1 的时候说明不符合操作条件,要放弃对象锁。
while(j == 1)
{
wait();
System.out.println();
}
j++;
System.out.println(j);
notify();
} //减少操作
public synchronized void sub() throws InterruptedException
{
     //当j = 0 的时候说明不符合操作条件,放弃对象锁
while(j == 0)
{
wait();
System.out.println();
}
j--;
System.out.println(j);
notify();
}
}

以上代码并不能一直循环执行,按道理说应该是一直循环执行的。

为什么呢????????

这就涉及到了notify() 和 notifyAll()的其中一个区别了。

这个区别就是:调用 notify() 方法只能随机唤醒一个线程,调用notifyAll() 方法的唤醒所有的等待的线程。

比如这里,当一个线程在正常执行。。。假设这里正常执行完一个增加操作的线程,然后调用 notify() 方法 那么它会随机唤醒一个线程。

  ①、如果唤醒的是进行减少操作的线程,此时 j = 1,线程能够正常执行减少操作。

  ②、如果唤醒的是进行增加操作的线程,此时 j = 1,那么不符合增加操作的条件,他就会调用 wait() 方法。那么调用完wait()方法后程序就会发现已经没有被唤醒的线程了。唯一一个被唤醒的线程因不符合条件放弃了对象锁,其他线程又没有被唤醒。此时程序只能一直等到其他线程被唤醒,但是它等不到了。

解决:

把notify() 改成notifyAll() 这个问题就解决了。因为如果唤醒一个线程,但是这个线程因不符合执行条件而放弃对象,还有很多唤醒的线程。

所以,当多个(两个以上的)线程操作同一个对象的时候最好使用的notifyAll(),这样就不会出现上述的问题了。


发现一个问题,既然使用notify()会出问题那为什么不在每个地方的使用notifyAll()呢??这二者还有其他我没了解的区别吗???难道使用notifyAll() 会使性能大大下降???有待解决。

关于notify() 和notifyAll() 一个需要注意的地方的更多相关文章

  1. 如何在 Java 中正确使用 wait, notify 和 notifyAll(转)

    wait, notify 和 notifyAll,这些在多线程中被经常用到的保留关键字,在实际开发的时候很多时候却并没有被大家重视.本文对这些关键字的使用进行了描述. 在 Java 中可以用 wait ...

  2. 线程同步以及 yield() wait()和notify()、notifyAll()

    1.yield() 该方法与sleep()类似,只是不能由用户指定暂停多长时间,并且yield()方法只能让同优先级的线程有执行的机会. 2.wait()和notify().notifyAll() 这 ...

  3. Java Thread wait, notify and notifyAll Example

    Java Thread wait, notify and notifyAll Example Java线程中的使用的wait,notify和nitifyAll方法示例. The Object clas ...

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

    Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ...

  5. 线程同步以及yield()、wait()、Notify()、Notifyall()

    一.线程同步 1.线程同步的目的是为了保护多个线程访问一个资源时对资源的破坏. 2.线程同步方法是通过锁来实现,每个对象都有切仅有一个锁,这个锁与一个特定的对象关联,线程一旦获取了对象锁,其他访问该对 ...

  6. 最简实例说明wait、notify、notifyAll的使用方法

    wait().notify().notifyAll()是三个定义在Object类里的方法,可以用来控制线程的状态. 这三个方法最终调用的都是jvm级的native方法.随着jvm运行平台的不同可能有些 ...

  7. 用实例揭示notify()和notifyAll()的本质区别

    用实例揭示notify()和notifyAll()的本质区别 收藏   notify()和notifyAll()都是Object对象用于通知处在等待该对象的线程的方法.两者的最大区别在于: notif ...

  8. java wait()和notify()、notifyAll()

    图见<JAVA并发编程的艺术>P98-101 这三个方法都是java.lang.Object的方法,用于协调多个线程对共享数据的存取,必须在synchronized语句块中使用!这三个方法 ...

  9. java notify和notifyAll的区别

    首先从名字可以了解,notify是通知一个线程获取锁,notifyAll是通知所有相关的线程去竞争锁. notify不能保证获得锁的线程,真正需要锁,并且可能产生死锁. 举例1: 所有人(消费者线程) ...

随机推荐

  1. Atom编辑器汉化

    Atom编辑器汉化成中文版 其他分享 7个月前 (04-04) 426浏览 0评论 Atom 是 Github 专门为程序员推出的一个跨平台文本编辑器.小松今天看到了这个编辑器,而且感觉不错,推荐一下 ...

  2. 关于vue的常识问题及解决方法

    一.VSCode开发必备插件 1.Beautify:语法高亮: 2.Bracket Pair Colorizer :对括号对进行着色: 3.ESLint:ESLint插件,高亮提示: 4.HTML C ...

  3. 【httpwatch】httpwatch对测试的应用

    HttpWatch是一款网页数据分析工具,是浏览器插件,集成在IE浏览器的工具栏中.主要可以用来帮忙我们查看及分析HTTP请求的:Cookie.请求参数.请求头信息.响应头信息.响应状态.响应正文等内 ...

  4. ngnix优化【转】

    nginx的优化 1. gzip压缩优化 2. expires缓存有还 3. 网络IO事件模型优化 4. 隐藏软件名称和版本号 5. 防盗链优化 6. 禁止恶意域名解析 7. 禁止通过IP地址访问网站 ...

  5. C#学习笔记15

    1.平台互操作性和不安全的代码:C#功能强大,但有些时候,它的表现仍然有些“力不从心”,所以我们只能摒弃它所提供的所有安全性,转而退回到内存地址和指针的世界. C#通过3种方式对此提供支持. (1)第 ...

  6. javascript刷新页面的集中办法

    1. history.go(0) 2. location.reload() 3. location=location 4. location.assign(location) 5. document. ...

  7. vs2015 web项目加载失败解决办法

    1.问题 ---------------------------Microsoft Visual Studio---------------------------Web 应用程序项目 XXWeb 已 ...

  8. nodejs项目windows下开机自启动

    Nodejs项目开机自启动 1. 在需要自启动的项目中安装 node-windows 模块 npm install node-windows --save 2. 在项目根目录创建nw.js文件 代码截 ...

  9. Spring 框架(一)

    1 spring框架概述 1.1 什么是spring l Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson 在其著作Expert ...

  10. 基础架构之Redis

    项目开发过程中,有些信息的变动频率是很低但又经常访问到,这些信息我们往往放在缓存中,目前在缓存组件中,Redis绝对值得你列入使用计划.更多详细信息可以参考官网 https://redis.io/.这 ...