2016-04-23 89 views
0

我目前正试图研究并发性,特别是“volatile”关键字。Java - 易失性不按预期工作

通过声明计数器变量挥发性所有写计数器变量将被立即写回主内存。此外,计数器变量的所有读取都将直接从主存储器读取。这里是计数器变量的volatile声明的外观

当一个线程写入volatile变量,那么不只是volatile变量本身被写入主存储器。在写入volatile变量之前,线程更改的所有其他变量也会刷新到主内存中。当一个线程读取一个易失变量时,它还会读取主内存中与易失变量一起刷新到主内存中的所有其他变量。

来源:tutorials.jenkov.com | Java Concurrency - Java Volatile Keyword

这使我得出结论/假设,我做出一个volatile变量的任何变化都会永远是所有线程可见。所以,我做了一个代码来测试它。

TestClass

package org.personal.test1; 

class TestClass { 
    public static int w = 0; 
    public static int x = 0; 
    public static int y = 0; 
    public static volatile int z = 0; 
    private static final int ITERATIONS = 100000; 


    public static void sooPlus(int indents) { 
     for (int i = 0; i < TestClass.ITERATIONS; i++) { 
      TestClass.w++; 
      TestClass.x++; 
      TestClass.y++; 
      TestClass.z++; 
     } 
    } 

    public static void sooMinus(int indents) { 
     for (int i = 0; i < TestClass.ITERATIONS; i++) { 
      TestClass.w--; 
      TestClass.x--; 
      TestClass.y--; 
      TestClass.z--; 
     } 
    } 


    public static synchronized String getVariableValues() { 
     StringBuilder stringBuilder = new StringBuilder(); 
     stringBuilder.append("("); 
     stringBuilder.append("w : "+TestClass.w+", "); 
     stringBuilder.append("x : "+TestClass.x+", "); 
     stringBuilder.append("y : "+TestClass.y+", "); 
     stringBuilder.append("z : "+TestClass.z+")"); 
     return stringBuilder.toString(); 
    } 

} 

Main Class

package org.personal.test1; 

/** 
* <ol type="I"> 
*  <li> 
*   <a href="http://tutorials.jenkov.com/java-concurrency/volatile.html">jenkov.com - Java Volatile Keyword</a> 
*  </li> 
* </ol> 
*/ 
public class Main { 

    public static void main(String[] args) { 
     Main.call1(); 
    } 

    private static void call1() { 
     Main.test1(); 
    } 

    private static void test1() { 
     Thread thread1 = new Thread("Thread1") { 
      @Override 
      public void run() { 
       TestClass.sooPlus(1); 
      } 
     }; 

     Thread thread2 = new Thread("Thread2") { 
      @Override 
      public void run() { 
       TestClass.sooMinus(4); 
      } 
     }; 

     thread1.start(); 
     thread2.start(); 

     try { 
      thread1.join(); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 

     try { 
      thread2.join(); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 

     System.out.println(TestClass.getVariableValues()); 

    } 
} 

我得到的结果不是我所期待的。

我得到什么(变化)

(w : -2314, x : -1692, y : -1416, z : -1656) 

我很期待

(w : 0, x : 0, y : 0, z : 0) 

或至少

(w : -2314, x : -1692, y : -1416, z : 0) 

的问题

  1. 我是怎么假设/推断错误,导致不同的输出少于预期?
  2. 我的测试方法不正确?如果是,那我该如何解决?
  3. 可选)有什么好的Java并发性教程,你推荐?

注意

  • 我没有尝试读取类似的问题,但我没能完全理解提问者试图以了解他的问题做。
+1

volatile关键字提供了一种弱的线程安全形式。它保证可见性,但不是原子性或互斥。是否可能会使易变的字段不是线程安全的?是的,如果写入字段不是原子的。由所有易失性字段组成的类是否可能不是线程安全的?是的,如果写入字段导致无效状态转换(由于缺乏同步)。 – scottb

回答

3

volatile关键字提供了弱线程安全的形式。它保证的可见性,但不是原子性或互斥

  • 是否可能使易失性字段不是线程安全的?线程安全。写入非易失性doublelong值不是原子的,而是写入易失性doublelong变量。

  • 是否有可能由所有易失性字段组成的类不是线程安全的?是的,如果写入字段导致无效状态转换(由于缺乏同步)。

(可选)有没有对Java并发任何好的教程,你 建议?

这本书通常被推荐为这个主题的权威性处理,是由Brian Goetz撰写的“Java Concurrency in Practice”。它变得有点老了。

+0

-1,对不起。这个答案大部分是正确的(特别是提到*互斥* - 你应该详细说明),但是volatile写* *保证是原子的(参见https://docs.oracle.com/javase/specs/ jls/se8/html/jls-17.html#jls-17.7),并且这个回答将(错误的)相反的要求放在前面和中心。 – ruakh

+0

我站好了。答案已修改。 – scottb

+0

谢谢; downvote缩回。尽管如此,对于OP的示例代码来说,真正重要的一点是,像TestClass.w - 这样的东西包含一个读操作,然后是一个完全独立的写操作,其结果是它不一定实际减少TestClass。 w'。 (它相当于'int tmp = TestClass.w; tmp--; TestClass.w = tmp;',这意味着它可以取代/覆盖/放弃对TestClass.w的其他更改)。 – ruakh