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. git 自己创建了一个项目A,我的同事fork一个B,当我的项目更新的时候,怎么样在他fork的repo上进行相应的更新?

    先把B clone到本地 git clone B_REPOSITORY_URL 再cd到本地B的目录,把A作为一个remote加到本地的B中(一般命名为upstream) git remote add ...

  2. 自定义控件如何给特殊类型的属性添加默认值 z(转)

    自定义控件如何给特殊类型的属性添加默认值 z 定义控件如何给特殊类型的属性添加默认值了,附自定义GroupBox一枚 标题有点那啥,但确实能表达我掌握此法后的心情. 写自定义控件时往往会有一个需求,就 ...

  3. 记一个bug的排查过程---复盘

    公众号做了新需求:菜单的click事件,支持多条客服消息. 上线后,只有一个功能不好使,是点击菜单,预期发一条文本类型的客服消息. 实际操作时,点这个菜单项后,什么也没有发生. elk上看日志,也没有 ...

  4. 常用工具说明--Maven使用说明

    什么是Maven? 如今我们构建一个项目需要用到很多第三方的类库,如写一个使用Spring的Web项目就需要引入大量的jar包.一个项目Jar包的数量之多往往让我们瞠目结舌,并且Jar包之间的关系错综 ...

  5. jQuery 整体架构

    不同于 jQuery 代码各个模块细节实现的晦涩难懂,jQuery 整体框架的结构十分清晰,按代码行文大致分为如上图所示的模块. 初看 jQuery 源码可能很容易一头雾水,因为 9000 行的代码感 ...

  6. 阿里云配置HTTPS

    阿里云配置HTTPS 2018-05-29 16:00:56 by SemiconductorKING 最近申请域名配置了一下HTTPS协议,记录一下配置过程. 准备 备案过的域名,一个SSL证书(免 ...

  7. HttpContext在多线程异步调用中的使用方案

    1.在线程调用中,有时候会碰到操作文件之类的功能.对于开发人员来说,他们并不知道网站会被部署在服务器的那个角落里面,因此根本无法确定真实的物理路径(当然可以使用配置文件来配置物理路径),他们唯一知道的 ...

  8. ios下虚拟键盘出现"搜索"字样

    最近在开发过程中,发现用户输入想要检索的内容,弹出虚拟键盘,在安卓机上虚拟键盘最右下角会有‘搜索’字样,而ios上虚拟键盘最右下角只有‘换行’字样, 这样用户体验就会大打折扣. 安卓机上虚拟键盘 io ...

  9. ASID 与 MIPS 中 TLB 相关

    ASID 为了提高TLB的性能,将TLB分成Global和process-specific.global 是指常驻在tlb中不会被刷出的,例如内核空间的翻译,process-specific 是指每个 ...

  10. IntelliJ IDEA 快捷键(二)(window版)

    一.重构 1.重构变量 修改变量名称,即重命名.快捷键 Shift + F6 ,位于 Refactor 中. 2.重构方法 可以增加变量个数.快捷键 Ctrl + F6 ,位于 Refactor 中. ...