首页 > 开发 > Java > 正文

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

2024-07-13 09:55:52
字体:
来源:转载
供稿:网友

这篇文章主要介绍了浅谈Java多线程编程中Boolean常量的同步问题,主要针对线程之间同步了不同的布尔对象的问题,需要的朋友可以参考下

在JAVA中通过synchronized语句可以实现多线程并发。使用同步代码块,JVM保证同一时间只有一个线程可以拥有某一对象的锁。锁机制实现了多个线程安全地对临界资源进行访问。

同步代码写法如下:

代码1:

 

 
  1. Object obj = new Object();  
  2. ...  
  3. synchronized(obj) {  
  4. //TODO: 访问临界资源  
  5. }  

JAVA的多线程总是充满陷阱,如果我们用Boolean作为被同步的对象,可能会出现以下两种情况:

一. 以为对一个对象加锁,实际同步的是不同对象。

代码2:

 

 
  1. private volatile Boolean isTrue = false;  
  2.  
  3. publich void aMethod() {  
  4. ...  
  5. synchronized(isTrue) {  
  6. isTrue = !isTrue;  
  7. //TODO: 访问临界资源  
  8. isTrue = !isTrue;  
  9. }  
  10. ...  
  11. }  

咋一看上面的代码没有问题,由于使用了synchronized(isTrue)同一时间只能有一个线程访问临界资源,但事实并不是这样。因为false和true这两个常量对应着两个不同的对象。当isTrue产生变化时,很可能导致不同的线程同步了不同的对象。JAVA的自动装箱会将false变为Boolean.FALSE,将true变为Boolean.TRUE(同时这也说明了此处若将false改为Boolean.FALSE其结果也是一样的)。写一个以上情况的测试代码如下:

代码3:

 

 
  1. public class BooleanTest {  
  2.  
  3. private volatile Boolean isTrue = Boolean.FALSE; //此处用false也一样  
  4.  
  5. public void aMethod() {  
  6. for(int i=0;i<10;i++) {  
  7. Thread t = new Thread() {  
  8. public void run() {  
  9. synchronized(isTrue) {  
  10. isTrue = !isTrue;  
  11. System.out.println(Thread.currentThread().getName() + " - isTrue=" + isTrue);  
  12. try{  
  13. Double ran = 1000 * Math.random();  
  14. Thread.sleep(ran.intValue());  
  15. }catch(InterruptedException e) {}  
  16.  
  17. if(!isTrue) System.out.println(Thread.currentThread().getName() + " - Oh, No!");  
  18.  
  19. isTrue = !isTrue;  
  20. }  
  21. }  
  22. };  
  23. t.start();  
  24. }  
  25. }  
  26.  
  27. public static void main(String... args) {  
  28. BooleanTest bt = new BooleanTest();  
  29. bt.aMethod();  
  30. }  
  31. }  

运行以上代码,不时的会看到 " - Oh, No!",表示不同的线程同时进入到synchronized代码块中。

二. 以为同步的是不同对象,实际是一个对象。

有时候我们可能希望在多个对象上进行同步,如果使用了Boolean作为被同步对象,很可能会导致本来应该没有关系的两个同步块使用了相同对象的锁。示例如下:

代码4:

 

 
  1. private volatile Boolean aBoolean = Boolean.FALSE;  
  2.  
  3. private volatile Boolean anotherBoolean = false;  
  4.  
  5. public void aMethod() {  
  6. ...  
  7. synchronized(aBoolean) {  
  8. //TODO: 访问临界资源1  
  9. }  
  10. ...  
  11. }  
  12.  
  13. public void anotherMethod() {  
  14. ...  
  15. synchronized(anotherBoolean) {  
  16. //TODO: 访问临界资源2  
  17. }  
  18. ...  
  19. }  

假设原本aMethod和anotherMethod分别会被两组没有关系的线程调用。但是由于Boolean.FALSE和false指向的是同一个对象,可能导致对临界资源2的访问被临界资源1阻塞了(反之亦然)。

以上两种情况说明,在使用同步块时,尽量不用使用Boolean对象作为被同步对象,不然可能会出现意想不到的问题,或者对以后的代码修改造成陷阱。

从此也可以看出,任何对常量的同步都是有风险的。如果一定要对 Boolean 进行同步,一定要用 new 操作符来创建 Boolean 对象。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表