2

我有一個關於java中同步的問題。在下面的Java程序中,我沒有得到任何輸出。但是,如果我從方法IFoo.s()中刪除同步語句,我會得到一些輸出。看起來IFoo.setP()和IFoo.s()方法是相互同步的。但'synchronized'只能防止兩個線程同時調用synchronized方法,對嗎?Java Synchronized同步所有同步的類之間的方法?

package com.example.relectiontest; 

import java.awt.Point; 
import java.util.Random; 

public class Main { 

public static void main(String[] args) throws Exception{ 
    final IFoo f = new IFoo(); 
    Runnable r = new Runnable() { 
     public void run() { 
      Random r = new Random(); 
      int a = r.nextInt(5)+1; 
      for(int i=0;i<1000000;++i){ 
       f.setP(a); 
      } 
     } 
    }; 
    Runnable r2 = new Runnable() { 
     public void run() { 
      for(int i=0;i<1000000;++i){ 
       f.s(); 
      } 
     } 
    }; 
    Thread T1 = new Thread(r, "T1"); 
    Thread T2 = new Thread(r, "T2"); 
    Thread T3 = new Thread(r2, "T3"); 
    T3.start(); 
    T1.start(); 
    T2.start(); 
} 

private static class IFoo{ 
    private Point p = new Point(); 

    public synchronized void setP(int a){ 
     //System.out.println("p1 "+Thread.currentThread()); 
     p.x = a; 
     p.y = p.x; 
     int x = p.x , y = p.y; 
     if(x != y) 
      System.out.println(Thread.currentThread()+"\t"+x+" "+y); 
     //System.out.println("p2 "+Thread.currentThread()); 
    } 

    public synchronized void s(){ 
     //System.out.println("s"); 
     p.x = 0; 
    } 
} 
} 

那麼,爲什麼我不能看到任何輸出?

問候

+0

你知道你的'System.out'調用在'IFoo.s()'中被註釋掉了嗎? –

+2

'synchronized'實例方法將所有*一起同步到同一個實例*。 – Holger

回答

3

在Java中,所有​​調用都在一個對象上同步。例如方法,他們反對的是類實例 - 所以在你的情況下,setPs都在IFoo的實例上同步。

這允許您控制對通過多種方法訪問的共享字段的訪問。使用你的代碼,這將正是你所需要的 - 你需要確保你沒有一個線程在setP改變狀態,而另一個在s正在讀取它。

如果您需要更細的控制,你可以使用synchronized塊,它允許你指定的對象鎖定在:

private final Object o=new Object(); 

public void method(){ 
    synchronized (o){ 
     //Synchronized code 
    } 
} 

這是一般generally recommended approach - 它允許你封裝你的鎖,所以你不不要冒險讓其他一些組織干擾你的鎖定,並且可能會對你的代碼執行代碼。

靜態方法在類對象上同步(例如IFoo.class)。

1

documentation

使得這些方法同步有兩個作用:

首先,這是不可能的同一對象的同步方法交錯兩個調用。當一個線程正在執行對象的同步方法 時,所有其他線程將爲同一個對象塊(暫停執行) 調用 同步方法,直到第一個線程完成對象。其次,當一個同步方法退出時,它會自動建立與同一對象的任何後續調用同步方法的before-before關係。此 保證對所有 線程都可見對象狀態的更改。

0

因爲通常你不應該得到任何輸出,因爲x應該等於y。 但是,當您刪除synchronized關鍵字時,兩個線程同時執行,如果s()在p.y = p.x和x = p.x語句之間執行,則可能會得到輸出。

4

因爲感謝同步x != y永遠不會是真的。

在您的非同步版本中,s()有可能每隔一段時間將p.x設置爲0(即使它未正確同步)。

在同步s()版本必須等到setP完成(因爲他們都同步,共享隱含this鎖),並且由於在setP邏輯的條件不可能是真實的。

您的示例過於複雜。你可以把它寫出來如下(在這兩種方法中添加同步地看到,什麼都不會被打印):

private static class IFoo { 
    volatile int x = 0; 
    public void setP(int a) { 
     x = a; 
     if(x != a) 
      System.out.println("Someone changed x!"); 
    } 

    public void s() { 
     x = 0; 
    } 
} 

還要注意的是靜態同步方法Class對象上進行同步,因爲它們沒有this。因此,實例和靜態方法不會相互鎖定,除非您明確地在一個通用鎖上進行同步。

if(x != y) 

由於線路:

p.x = a; 
p.y = p.x; 
int x = p.x , y = p.y; 

你讓X == y爲不顯示輸出

1

只有當輸出將被顯示出來。

從s方法中刪除同步關鍵字 - 線程有時會將x設置爲0,這會使if(x!= y)爲true。並且輸出是可見的。