2016-08-14 89 views
5

我已經寫了一些多線程代碼在java和同步的方法,改變了變量,但它不同步我的代碼,我仍然得到隨機值。這裏是我的代碼:爲什麼在此多線程程序中不同步訪問同步方法?

public class Main { 
    public static void main(String[] args) throws Exception { 
     Resource.i = 5; 
     MyThread myThread = new MyThread(); 
     myThread.setName("one"); 
     MyThread myThread2 = new MyThread(); 
     myThread.start(); 
     myThread2.start(); 
     myThread.join(); 
     myThread2.join(); 
     System.out.println(Resource.i); 
    } 
} 
class MyThread extends Thread { 
    @Override 
    public void run() { 
     synMethod(); 
    } 

    private synchronized void synMethod() { 
     int i = Resource.i; 
     if(Thread.currentThread().getName().equals("one")) { 
      Thread.yield(); 
     } 
     i++; 
     Resource.i = i; 
    } 
} 

class Resource { 
    static int i; 
} 

有時候我7,有時6,但我已經同步synMethod,據我所知沒有線程應該在這個方法,而其他線程執行這一點,所以操作應該是原子,但他們不是,我不明白爲什麼?你能否向我解釋,並回答 - 我該如何解決?

+2

你同步的問題鎖定什麼。我建議你不要使用子類Thread,因爲這會導致令人驚訝的結果。 –

回答

10

添加​​方法就像在this上同步。既然你有兩個不同的線程實例,它們不會彼此鎖定,並且這種同步並沒有真正做任何事情。

爲了使同步生效,您應該在某些共享資源上進行同步。在你的榜樣,Resource.class可以由一個不錯的選擇:

private void synMethod() { // Not defined as synchronized 
    // Synchronization done here: 
    synchronized (Resource.class) { 
     int i = Resource.i; 
     if (Thread.currentThread().getName().equals("one")) { 
      Thread.yield(); 
     } 
     i++; 
     Resource.i = i; 
    } 
} 
+0

對Resource.i的讀訪問權限也必須同步,以確保更新的正確可見性。 –

+0

@ J.B你的意思是,閱讀'Resource.i' _after_'join()'這兩個線程?那麼'join()'會創建一個[happen-before relation](https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.4.5)(_All線程中的動作發生 - 在任何其他線程從該線程上的join()成功返回之前),以便該部分正確。 – Roman

1

讓我們從Oracle文檔頁面來看看synchronized methods定義。

製作方法​​有兩個作用:

首先,它是不可能的同一對象來交織上同步方法的兩個調用。當一個線程正在執行一個對象的同步方法時,所有其他線程調用同一對象的同步方法塊(掛起執行),直到第一個線程完成對象。

回來到您的查詢:

synMethod()是一個同步的方法對象層級。兩個線程訪問相同的​​方法以順序方式獲取對象鎖。但是,在沒有共享鎖的情況下,訪問不同實例(對象)的同步方法的兩個線程將異步運行。

myThreadmyThread2是兩個不同的對象=>內部鎖是在兩個不同的對象中獲取的,因此您可以異步訪問這些方法。

一個解決方案:如Mureinik引用的,使用共享對象進行鎖定。

其他解決方案(S):使用更好的併發結構,如ReentrantLock

您找到相關SE問題幾個備選方案:

Avoid synchronized(this) in Java?