1

在中below code listings,语句 1 和语句 2 线程是否安全?他们正在使用VolatileIntWrapper.

如果它们不是线程安全的,哪些语句需要包装在同步块中?

public class Demo {

    public static void main(String[] args) {

        VolatileIntWrapper volatileIntWrapper = new VolatileIntWrapper() ;

        for(int i = 1 ; i <= 5 ; ++i){
            new ModifyWrapperIntValue(volatileIntWrapper).start() ; 
        }
    }
}

class VolatileIntWrapper{
    public volatile int value = 0 ;
}

class ModifyWrapperIntValue extends Thread{

    private VolatileIntWrapper wrapper ;
    private int counter = 0 ;

    public ModifyWrapperIntValue(VolatileIntWrapper viw) {
        this.wrapper = viw ;
    }

    @Override
    public void run() {

        //randomly increments or decrements VolatileIntWrapper primitive int value

        //we can use below statement also, if value in VolatileIntWrapper is private
        // wrapper.getValue() instead of wrapper.value
        //but, as per my understanding, it will add more complexity to logic(might be requires additional synchronized statements),
        //so, for simplicity, we declared it public


        //Statement 1
        while(wrapper.value > -1500 && wrapper.value < 1500){
            ++counter ;
            int randomValue = (int) (Math.random() * 2) ;

            //Statement 2
            wrapper.value += (randomValue == 0) ?   1       :       -1 ;
        }

        System.out.println("Executed " + counter + " times...");
    }
}
4

4 回答 4

4

volatile关键字为读取和写入字段提供了内存屏障。这意味着多个线程可以访问该字段并保证读取最新值,并且保证其他线程可以看到它们的写入。

volatile不做是围绕操作顺序提供任何保证——尤其是当您有多个读写语句时。在您的代码中,您正在访问volatile int循环中的几个地方:

    while(wrapper.value > -1500 && wrapper.value < 1500){
        ...
        wrapper.value += (randomValue == 0) ?   1       :       -1 ;
    }

这里不保证操作的顺序。在线程 A 测试之后value > -1500,另一个线程可能会在线程 A 测试之前value < 1500立即更改它。或者线程 A 可能会进行两个测试,然后线程 B 可能会进行两个测试,然后线程 A 将分配值,然后线程 B 将分配值。这就是多线程竞争条件的本质。

while循环是我怀疑会被认为有错误的代码部分,除非您围绕它进行同步。您应该执行以下操作。同步该部分后,synchronized关键字本身就提供了内存屏障,因此volatile关键字是不必要的。

   synchronized (wrapper) {
       while (...) {
         ...
       }
   }
于 2012-07-13T05:56:53.123 回答
2

使用 volatile 字段一次且仅一次是安全的。(读取和写入算作两次)

您总共使用了四次该字段,因此您有三个位置用于比赛条件。

这个例子的问题是单线程执行更快更简单,所以你用多线程的方式做任何事情都会显得不自然和低效。

于 2012-07-13T05:57:07.413 回答
0

这个问题需要以下解释:

您使用的线程是安全的,并且您正在按预期读取原始值。

在原始字段上使用同步块有一个特定的术语,但您需要执行以下操作:

  • 使用您所在领域的 getter 和 setter。
  • 将同步放在两个访问器中,瞧。
于 2012-07-13T06:05:33.863 回答
0

Java In Concurrencty表示使用 volatile 变量需要满足以下条件:

1.  Writes to the variable do not depend on its current value, or you can ensure that only a single thread ever updates the value;

2.  The variable does not participate in invariants with other state variables; and

3.  Locking is not required for any other reason while the variable is being accessed.
于 2012-07-13T10:36:18.853 回答