浅谈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 对象。

标签:Java,布尔
0
投稿

猜你喜欢

  • java 较大数据量取差集,list.removeAll性能优化详解

    2022-11-06 16:52:15
  • Java使用entrySet方法获取Map集合中的元素

    2022-08-06 17:41:05
  • Java 使用 FFmpeg 处理视频文件示例代码详解

    2023-03-19 00:54:37
  • Java实现AOP功能的封装与配置的小框架实例代码

    2022-12-27 10:57:45
  • Java 反射机制实例详解

    2023-11-28 03:49:47
  • Java实现数组反转翻转的方法实例

    2021-09-19 00:21:26
  • Android多国语言转换Excel及Excel转换为string详解

    2022-10-30 08:07:02
  • C#实现日期格式转换的公共方法类实例

    2023-05-16 10:06:54
  • eclipse构建和发布maven项目的教程

    2022-01-12 21:41:00
  • 编写简易Android天气应用的代码示例

    2023-10-16 18:30:36
  • Android隐藏顶部状态栏所遇到的问题

    2023-08-02 03:39:26
  • SpringCloud实现灰度发布的方法步骤

    2023-03-17 05:18:37
  • C# 最基础知识介绍--多态

    2022-07-10 14:34:36
  • C#基于TCP协议的服务器端和客户端通信编程的基础教程

    2023-08-20 13:09:55
  • Android实现快递单号查询快递状态信息

    2023-04-03 14:51:14
  • 全面理解java中的异常处理机制

    2023-10-26 04:08:20
  • Android实现将已发送的短信写入短信数据库的方法

    2021-08-20 05:40:57
  • C#基于OLEDB获取Excel文件表结构信息的方法

    2022-03-06 07:29:44
  • android使用AsyncTask实现多线程下载实例

    2023-02-02 16:30:04
  • Spring注解配置实现过程详解

    2022-04-06 11:12:52
  • asp之家 软件编程 m.aspxhome.com