关于notify() 和notifyAll() 一个需要注意的地方
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() 一个需要注意的地方的更多相关文章
- 如何在 Java 中正确使用 wait, notify 和 notifyAll(转)
wait, notify 和 notifyAll,这些在多线程中被经常用到的保留关键字,在实际开发的时候很多时候却并没有被大家重视.本文对这些关键字的使用进行了描述. 在 Java 中可以用 wait ...
- 线程同步以及 yield() wait()和notify()、notifyAll()
1.yield() 该方法与sleep()类似,只是不能由用户指定暂停多长时间,并且yield()方法只能让同优先级的线程有执行的机会. 2.wait()和notify().notifyAll() 这 ...
- Java Thread wait, notify and notifyAll Example
Java Thread wait, notify and notifyAll Example Java线程中的使用的wait,notify和nitifyAll方法示例. The Object clas ...
- Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition
Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ...
- 线程同步以及yield()、wait()、Notify()、Notifyall()
一.线程同步 1.线程同步的目的是为了保护多个线程访问一个资源时对资源的破坏. 2.线程同步方法是通过锁来实现,每个对象都有切仅有一个锁,这个锁与一个特定的对象关联,线程一旦获取了对象锁,其他访问该对 ...
- 最简实例说明wait、notify、notifyAll的使用方法
wait().notify().notifyAll()是三个定义在Object类里的方法,可以用来控制线程的状态. 这三个方法最终调用的都是jvm级的native方法.随着jvm运行平台的不同可能有些 ...
- 用实例揭示notify()和notifyAll()的本质区别
用实例揭示notify()和notifyAll()的本质区别 收藏 notify()和notifyAll()都是Object对象用于通知处在等待该对象的线程的方法.两者的最大区别在于: notif ...
- java wait()和notify()、notifyAll()
图见<JAVA并发编程的艺术>P98-101 这三个方法都是java.lang.Object的方法,用于协调多个线程对共享数据的存取,必须在synchronized语句块中使用!这三个方法 ...
- java notify和notifyAll的区别
首先从名字可以了解,notify是通知一个线程获取锁,notifyAll是通知所有相关的线程去竞争锁. notify不能保证获得锁的线程,真正需要锁,并且可能产生死锁. 举例1: 所有人(消费者线程) ...
随机推荐
- Xcode日常使用
1.构建输出目录(Preferences->Locations->Locations)当选择为Default时,Derived Data的目录为~/Library/Developer/Xc ...
- UOJ #138. 【UER #3】开学前的涂鸦
Description 红包是一个有艺术细胞的男孩子. 红包由于NOI惨挂心情不好,暑假作业又多,于是他开始在作业本上涂鸦. 一开始,他在纸上画了一棵 n 个节点的树.但是他觉得这样的画太简单了,体现 ...
- bitbucket 源代码托管
5个人以下可以免费使用,不限制仓库的数量; 国外的注册需要开启蓝灯FQ; 1.注册账号 maanshancss w1-g1@qq.com;创建仓库; 然后拷贝现有项目 然后提交 然后push; 2.写 ...
- mac 好用软件地址存储
Navicat Premium 12.0.24 for mac已破解中文 https://www.52pojie.cn/thread-727433-1-1.html sublime 破解方法https ...
- 冒泡排序——Python实现
一.排序思想 排序思想参见:https://www.cnblogs.com/luomeng/p/10161794.html 二.python实现 def bubble_sort(nums): &quo ...
- 由ArrayList来深入理解Java中的fail-fast机制
1. fail-fast简介“快速失败”也就是fail-fast,它是Java集合的一种错误检测机制.某个线程在对collection进行迭代时,不允许其他线程对该collection进行结构上的修改 ...
- Google安装postman插件
1.保证网上商店可用 http://jingyan.baidu.com/article/48a42057ea53a1a9242504c1.html
- Js获取request中的对象的属相值
将这个值放在页面上.再取出来. 在body中定义隐藏属性的input按钮来接收request中的值: <input type="hidden" value="< ...
- Java序列话和反序列化理解(New)
public interface Serializable {} 该接口没有任何实现方法,是一种标志,instance of Serializable 会判断object类型 一.序列化和反序列化的 ...
- Python爬虫教程-22-lxml-etree和xpath配合使用
Python爬虫教程-22-lxml-etree和xpath配合使用 lxml:python 的HTML/XML的解析器 官网文档:https://lxml.de/ 使用前,需要安装安 lxml 包 ...