浅谈Java多线程编程中Boolean常量的同步问题
作者:johnshen0708 发布时间:2021-06-20 16:36:26
在JAVA中通过synchronized语句可以实现多线程并发。使用同步代码块,JVM保证同一时间只有一个线程可以拥有某一对象的锁。锁机制实现了多个线程安全地对临界资源进行访问。
同步代码写法如下:
代码1:
Object obj = new Object();
...
synchronized(obj) {
//TODO: 访问临界资源
}
JAVA的多线程总是充满陷阱,如果我们用Boolean作为被同步的对象,可能会出现以下两种情况:
一. 以为对一个对象加锁,实际同步的是不同对象。
代码2:
private volatile Boolean isTrue = false;
publich void aMethod() {
...
synchronized(isTrue) {
isTrue = !isTrue;
//TODO: 访问临界资源
isTrue = !isTrue;
}
...
}
咋一看上面的代码没有问题,由于使用了synchronized(isTrue)同一时间只能有一个线程访问临界资源,但事实并不是这样。因为false和true这两个常量对应着两个不同的对象。当isTrue产生变化时,很可能导致不同的线程同步了不同的对象。JAVA的自动装箱会将false变为Boolean.FALSE,将true变为Boolean.TRUE(同时这也说明了此处若将false改为Boolean.FALSE其结果也是一样的)。写一个以上情况的测试代码如下:
代码3:
public class BooleanTest {
private volatile Boolean isTrue = Boolean.FALSE; //此处用false也一样
public void aMethod() {
for(int i=0;i<10;i++) {
Thread t = new Thread() {
public void run() {
synchronized(isTrue) {
isTrue = !isTrue;
System.out.println(Thread.currentThread().getName() + " - isTrue=" + isTrue);
try{
Double ran = 1000 * Math.random();
Thread.sleep(ran.intValue());
}catch(InterruptedException e) {}
if(!isTrue) System.out.println(Thread.currentThread().getName() + " - Oh, No!");
isTrue = !isTrue;
}
}
};
t.start();
}
}
public static void main(String... args) {
BooleanTest bt = new BooleanTest();
bt.aMethod();
}
}
运行以上代码,不时的会看到 " - Oh, No!",表示不同的线程同时进入到synchronized代码块中。
二. 以为同步的是不同对象,实际是一个对象。
有时候我们可能希望在多个对象上进行同步,如果使用了Boolean作为被同步对象,很可能会导致本来应该没有关系的两个同步块使用了相同对象的锁。示例如下:
代码4:
private volatile Boolean aBoolean = Boolean.FALSE;
private volatile Boolean anotherBoolean = false;
public void aMethod() {
...
synchronized(aBoolean) {
//TODO: 访问临界资源1
}
...
}
public void anotherMethod() {
...
synchronized(anotherBoolean) {
//TODO: 访问临界资源2
}
...
}
假设原本aMethod和anotherMethod分别会被两组没有关系的线程调用。但是由于Boolean.FALSE和false指向的是同一个对象,可能导致对临界资源2的访问被临界资源1阻塞了(反之亦然)。
以上两种情况说明,在使用同步块时,尽量不用使用Boolean对象作为被同步对象,不然可能会出现意想不到的问题,或者对以后的代码修改造成陷阱。
从此也可以看出,任何对常量的同步都是有风险的。如果一定要对 Boolean 进行同步,一定要用 new 操作符来创建 Boolean 对象。
猜你喜欢
- 注意我这里用的是官方最稳定的版本3.7.1,版本之间有个别命令是有差距的!1.zkCli.sh客户端zkCli.sh可以理解成客户端,也可以
- 将JavaDoc 注释 生成API文档1. 打开java代码,编写JavaDoc 注释,只有按照java的规范编写注释,才能很好的生成API
- 在这篇文章中,我将向您展示如何用新的Java 8 forEach语句循环一个List和Map。1、forEach 和 Map1.1、常规循环
- 在 Java 语言中的类初始化块 文章中我们简单的介绍了下 Java 中的实例初始化块 ( IIB )。不过我觉得介绍的有点简单了,于是,再
- PS:本文包含了大部分strings函数的说明,并附带举例说明。本来想自己整理一下的,发现已经有前辈整理过了,就转了过来。修改了原文一些源码
- 一 前言学习微服务要从基础的架构学起,首先你要有个微服务的概念才能学习对吧!!如果你都不知道啥是微服务,就一头扎进去学习,你自己也觉得自己也
- 现在提起Android开发工具,大多人第一个想到的肯定是Android Studio。谷歌专门为Android开发者推出的这款IDE,以其强
- 1.基本介绍Java自带日期格式化工具DateFormat ,但是DateFormat 的所有实现,包括 SimpleDateFormat
- 目录1、如果一个方法或变量是"private"访问级别,那么它的访问范围是:2、代码将打印?3、下面关于hibernat
- 本文实例为大家分享了OpenCV实现直线检测并消除的具体代码,供大家参考,具体内容如下很简单,代码如下#include<iostrea
- 在平常工作中我们经常会遇到maven引用的jar包冲突的事情,这时候我们就需要找出冲突的包,并将低版本或者缺少某些方法的jar给剔除掉。这个
- logback输出日志屏蔽quartz的debug等级日志在一个spring的老项目中,使用了logback来作为日志管理,logback.
- 1.多数元素题目描述思路详解这个思路比较简单,先排序,排序过后遍历如果后一个等于前一个输出就好代码与结果class Solution { &
- 使用开源项目JAVAE 进行视频格式转换JAVAE简介:JAVE (Java音频视频编码器)库是ffmpeg项目的Java包装器。开发人员可
- 【程序1】 题目:有1、2、3、4个数字,能组成多少个互不相同且无重复数字的三位数?都是多少? 1.程序分析:可填在百位、十位、个位的数字都
- 本文实例讲述了Java设计模式之抽象工厂模式。分享给大家供大家参考,具体如下:具体工厂类:生产创建某一类具体产品对象。抽象产品类可以使用接口
- Java 异常的栈轨迹(Stack Trace)详解 捕获到异常时,往往需要进行一些处理。比较简单直接的
- 一般数据库的编码是utf8,utf8是不支持存储表情符的,当存入的微信昵称带有表情符时就会出现乱码情况,有两种解决方法:1.mysql数据库
- 使用Mybatis-Plus的SqlSessionFactory问题前些日子工作中出现一个问题,项目中使用了MybatisPlus,然后出现
- 栈栈(stack)又名堆栈,它是一种运算受限的线性表 。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。