浅谈Java多线程编程中Boolean常量的同步问题

所属分类: 软件编程 / java 阅读数: 43
收藏 0 赞 0 分享

在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 对象。

更多精彩内容其他人还在看

利用MultipartFile实现文件上传功能

这篇文章主要为大家详细介绍了利用MultipartFile实现文件上传功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
收藏 0 赞 0 分享

Java编程实现NBA赛事接口调用实例代码

这篇文章主要介绍了Java编程实现NBA赛事接口调用实例代码,具有一定参考价值,需要的朋友可以了解下。
收藏 0 赞 0 分享

Java编程之双重循环打印图形

这篇文章主要介绍了Java编程之双重循环打印图形,属于Java编程基础练习部分,具有一定参考价值,需要的朋友可以了解下。
收藏 0 赞 0 分享

java基础学习JVM中GC的算法

这篇文章主要介绍了java基础学习JVM中GC的算法,通过图文加深对GC算法思路的理解。
收藏 0 赞 0 分享

Java编程Post数据请求和接收代码详解

这篇文章主要介绍了Java编程Post数据请求和接收代码详解,涉及enctype的三种编码,post与get等相关内容,具有一定参考价值,需要的朋友可以了解下。
收藏 0 赞 0 分享

Retrofit+Rxjava实现文件上传和下载功能

这篇文章主要介绍了Retrofit+Rxjava实现文件上传和下载功能,文中提到了单文件上传和多文件上传及相关参数的请求,需要的朋友参考下吧
收藏 0 赞 0 分享

Retrofit+Rxjava下载文件进度的实现

这篇文章主要介绍了Retrofit+Rxjava下载文件进度的实现,非常不错,具有参考借鉴价值,需要的朋友可以参考下
收藏 0 赞 0 分享

java检查服务器的连通两种方法代码分享

这篇文章主要介绍了java检查服务器的连通两种方法代码分享,涉及ping的介绍以及检查服务器连通的两种方法代码示例,具有一定参考价值,需要的朋友可以了解下。
收藏 0 赞 0 分享

Java/Android 获取网络重定向文件的真实URL的示例代码

本篇文章主要介绍了Java/Android 获取网络重定向文件的真实URL的示例代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
收藏 0 赞 0 分享

java并发编程之同步器代码示例

这篇文章主要介绍了java并发编程之同步器代码示例,分享了相关代码,具有一定参考价值,需要的朋友可以了解下。
收藏 0 赞 0 分享
查看更多