关于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: 所有人(消费者线程) ...
随机推荐
- axios的兼容性
axios的兼容性处理 一.简介 看看官网的简介: “Promise based HTTP client for the browser and node.js” 译:基于 Promise 的 H ...
- ORM框架SQLAlchemy的使用
ORM和SQLAlchemy简介 对象关系映射(Object Relational Mapping,简称ORM),简单的来说,ORM是将数据库中的表与面向对象语言中的类建立了一种对应的关系.然后我们操 ...
- Node.js学习笔记(六) --- Nodejs 的非阻塞 I/O、 异步、 事件驱动
1. Nodejs 的单线程 非阻塞 I/O 事件驱动在 Java. PHP 或者.net 等服务器端语言中,会为每一个客户端连接创建一个新的线程.而每个线程需要耗费大约 2MB 内存.也就是说,理论 ...
- 互联网轻量级框架SSM-查缺补漏第六天【级联+延迟加载特辑】
简言:本来这是昨天看的,但是因为想好好写一下[级联]这个东西,所以就看完之后今天来整理一下. 级联 1. 什么是级联 级联是一个数据库实体的概念.比如教师就需要存在学生与之对应,这样就有教师学生表,一 ...
- JBPM学习第2篇:为Eclipse添加JBPM开发支持
1.Eclipse添加JBoss支持插件 参考:Eclipse添加JBoss支持 若已安装,直接跳过! 2.Eclipse添加Drools插件 jbpm-installer-full解压后的文件夹中找 ...
- asp.net 、C#实现微信企业号OAuth2认证
以微信企业号作为入口的应用,几乎都会遇到需要应用系统中个人信息和微信用户关联问题.从而进行其他业务处理.目前所做项目采取在企业号通讯录添加自定义字段存入应用系统用户信息表中唯一标识UserGuid进行 ...
- jQuery实现18位身份证输入隔位添加空格及格式验证
说明:jQuery实现身份证输入添加空格,表单验证身份证输入,并且输入时前6位添加一个空格,中间8位后添加一个空格,及身份证格式验证 参考:基于jquery实现的银行卡号每隔4位自动插入空格的实现代码 ...
- Google自写插件详解
谷歌插件详解,跳转至个人主页查看. GoogleExtension
- 织梦后台添加友情链接的方法(flink标签)
标记名称:flink[标签简介][功能说明]:用于获取友情链接,其对应后台文件为"includetaglibflink.lib.php".[适用范围]:全局标记,适用V55,V56 ...
- 归并排序算法Matlab实现
Matlab一段时间不用发现有些生疏了,就用归并排序来练手吧.代码没啥说的,百度有很多.写篇博客,主要是记下matlab语法,以后备查. 测试代码 srcData = [1,3,2,4,6,5,8 ...