欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2024-03-06 12:58:49
在java中通过synchronized语句可以实现多线程并发。使用同步代码块,jvm保证同一时间只有一个线程可以拥有某一对象的锁。锁机制实现了多个线程安全地对临界资源进行...

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