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. Restful的理解,Restful 优缺点

    写一下我对restful的理解,最近换工作面试的时候有问到我restful api的东西,工作中以前很多项目也是webapi + js前台控件的形式构建系统.实际上感觉restful太“理想化”,用起 ...

  2. PHP学习2——基本语法

    主要内容: 二进制 数据类型 变量 常量 赋值 语句结构 函数 网站的核心功能是展现信息,文字,图片,视频,音频,对于计算机来说都是数据,这些数据按照二进制进行存储. 二进制 就是1100,0100, ...

  3. sqlserver 自增ID插入指定数据(转)

    set identity_insert 表名 ON --允许对自增列Id插入指定数据 insert into table_name(Id,Name) values(1,'test') set iden ...

  4. 项目开发-->高级功能汇总

    祭奠曾经逝去的青春…… 1.高级功能汇总-->Memcached之ASP.NET实现 2.高级功能汇总-->HubbleDotNet软件安装

  5. JVM(三) 垃圾回收时间点和垃圾收集器

      收集器组合章节来自第一篇参考文章,非原创,作者总结地非常好!          分代收集相关概念来自参考文章第二篇,非原创         第二篇参考资料的文章质量很高,推荐阅读! 分代收集(Ge ...

  6. 实例化geoserver wms服务

    var vectorWmsJHdataLayer = new ol.layer.Tile({ source: new ol.source.TileWMS({ //地址 url:'http://loca ...

  7. 八、profile多环境配置

    通常我们的程序有着多个环境: 1.开发环境: 2.生产环境. 等 环境的配置各不相同,我们希望通过一个简单的配置来切换环境,而springboot轻松地实现了该功能: 一.多环境需要多配置文件 一般我 ...

  8. 简单的CRUD(二)

    一.重构简单的CRUD 1.JDBC工具类 1.因为在crud中都包含一些相同的代码所以可以提取出来,抽取代码重构为工具类. 2.将工具类设置为static静态类,方便调用,不需要new对象. pub ...

  9. hdu 4003 Find Metal Mineral 树形dp ,*****

    Find Metal Mineral Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65768/65768 K (Java/Other ...

  10. cocos-creator 脚本逻辑-2

    1.预制体 1)节点操作 Cc.find(‘node-1’) 获取节点 全局事件 作用于 canvas this.node.destroy() 删除节点(从内存中删除) 添加删除获取节点或组件 let ...