为什么变量在同步中对其他线程不可见?

时间:2016-10-06 16:46:17

标签: java multithreading

我们假设我有两个线程t1t2正在尝试访问incX()

以下是我的以下代码:

    class Test implements Runnable {
    private int x  = 0;

    public void incX() {
    synchronized(this) {
    x = ++x; 
    }
    System.out.println("x is: "+x+"     "+Thread.currentThread().getName());
   }

    public void run() {
    incX();
}

 public static void main(String[] args)   {

   Thread t1 = new Thread(new Test());
   t1.start();
   Thread t2 = new Thread(new Test());
   t2.start();

}

这是我的输出:

    x is: 1     Thread-1
    x is: 1     Thread-0

incX()方法一样,我已同步x = ++x,因此对线程t1所做的更改应该对线程t2可见,对吧?所以我的输出应该是:

    x is: 1     Thread-1
    x is: 2     Thread-0

我知道++x不是原子操作,但它是同步的,因此线程t2无法获取锁。因此,线程不应interleave,并且xt2所做的更改应该对线程 x is: 2 Thread-0 可见,对吧?我误会了吗? 所以我的问题是为什么我没有得到输出:

{{1}}

1 个答案:

答案 0 :(得分:9)

您正在使用Test类的两个单独的实例,因此每个x中的Test test1 = new Test(); Test test2 = new Test(); test1.incX(); test2.incX(); 只会增加一次。它实际上与此相同:

Test

由于x的每个实例都有自己的1,因此您也会看到class Test { private int x = 0; public void incX() { synchronized(this) { x = ++x; // See "Side Note" below } System.out.println("x is: "+x+" "+Thread.currentThread().getName()); } } public class Main { public static void main(String[] args) { Test test = new Test(); // One instance Thread t1 = new Thread(() -> { test.incX(); // Used by this thread }); Thread t2 = new Thread(() -> { test.incX(); // And also by this one }); t1.start(); t2.start(); try { t1.join(); t2.join(); } catch (Exception e) { } System.out.println("Done"); } } 两次该代码。

要测试对相同实例的同步访问,您需要使用单个实例。例如:

x

将输出

x is: 1     Thread-0
x is: 2     Thread-1
Done

或类似(当然,这取决于在什么时间安排的线程)。

有时,它甚至看起来像这样:

x is: 2     Thread-0
x is: 2     Thread-1
Done

这是因为System.out.println语句中对synchronized的访问权限在<{em>} x块之外,因此有时(并非总是)synchronized将会在println块结束后synchronized(this) { x = ++x; } // ***The other thread can jump in here and increment x System.out.println("x is: "+x+" "+Thread.currentThread().getName());

之前增加
t1

更详细:

  1. t2进入已同步的块
  2. t1尝试输入已同步的块,但必须等待,因为t1有锁
  3. x增加t1,使其成为1
  4. t2退出已同步的块
  5. x跳入并增加t2,使其成为2
  6. t1退出已同步的块
  7. x输出{em}当前值t2(2)
  8. x输出{em}当前值x(2)
  9. 请注意,步骤2和步骤3可以是任何顺序,步骤6-8也可以是任何顺序。

    为了在递增后可靠地报告同步块内的println,我们想要:

    1. public void incX() { synchronized(this) { x = ++x; // See "Side Note" below System.out.println("x is: "+x+" "+Thread.currentThread().getName()); } } 移动到同步块

      public void incX() {
          int y;                               // Local variable
          synchronized(this) {
              y = ++x;                         // No longer and odd thing to do
          }
          System.out.println("x is: "+y+"     "+Thread.currentThread().getName());
      }
      

    2. 将增量结果保存在局部变量

      ++x
    3. 除非你有一个非常好的理由在输出期间保持同步锁定,否则请使用#2。

      旁注:正如我在评论中提到的,x 已经将其值写回x =,这就是增量运算符的作用。

      x = ++x; 部分
      ++x;
      

      ......是不必要的。使用增量运算符:

      x += 1;
      

      ......或不要:

      public class prac {
          public static void main (String args []){
      String a = "wtf";
      String b = "wtf";
      System.out.println("WHY" + a==b);
      
          }
      
      }