2015-05-05 75 views
0

我可以在此獲得您的看法。是否同步兩個方法分別等效於同步調用這兩個方法的塊?

「鑑於這種情況下:這個類是爲了讓用戶寫一系列消息,讓每一個消息都被標識一個時間戳和寫消息的線程的名稱

public class Logger 
{ 
    private StringBuilder contents = new StringBuilder(); 

    public void log(String message) 
    { 
     contents.append(Thread.currentThread().getName()); 
     contents.append("message"); 
     contents.append("\n"); 
    } 

    public String getContents() 
    { 
     return contents.toString(); 
    } 
} 

如何我們可以確保這個類的實例可以被多個線程安全地使用嗎?「

顯然,正確的答案是「同步log()和getContents()」。

在我看來,這仍然不夠,因爲一個線程在調用getContent()方法時仍然可能拉出其他線程的消息。我錯了嗎?

在編寫消息和最終調用getContent()之間,線程是否會失去對另一個線程的鎖定?

例如在UtilityClass

Logger logger = new Logger(); 
在MyThread的

..

// Get logger instance 
Logger myLogger = utilityClass.getLogger(); 

// doSomething else, release lock on 'logger' instance 
// other thread gets the Logger from utility class, and invokes the log() method on it 

// MyThread now calls getContent(), getting the other thread's message 
myLogger.getContent() 

謝謝!

+0

定義「安全」。如果同步兩個方法,那麼:一個日誌消息不會被添加到另一個日誌消息中,'getContents'永遠不會返回一半的日誌消息(在最後切斷),並且它不會因爲併發問題而崩潰。 – immibis

+2

沒有人說任何線程都不應該看到其他線程的消息。也不需要在寫入數據和獲取內容之間保持鎖定。 – RealSkeptic

+0

不完全是因爲在同步黑色情況下,您正在限制用戶從同步塊調用。但用戶可能會同步調用這些方法,所以更好的方法是使方法同步。 – Prashant

回答

0

事實上,如果只有方法是同步的,只有方法調用將是原子的。你真的需要記錄器針對某個線程嗎?如果是這樣,你可能想要改變你的設計,以適應這種需要,例如使用ThreadLocal 真的需要線程劃分。

+0

因此,同步這兩種方法不會使類線程安全正確嗎? – paidedly

+0

這與給定的賦值無關,它明確收集來自多個線程的消息,並且不打算成爲單個線程的日誌(否則,爲什麼要使用線程標識符標記消息?) – RealSkeptic

+1

@ user3678484:我認爲你很困惑什麼是線程安全。線程安全性不是類的全部屬性或非屬性(例如,一個類可能有條件或無條件線程安全)。您需要爲您的類確定一個線程安全策略,這在您的應用程序的上下文中是有意義的。 – scottb

0

隨着您發佈的代碼(非同步),顯然有一個點(標記如下),如果getContents被稱爲它會收到一個不完整的記錄。顯然,必須添加一些同步。

public class Logger { 

    private StringBuilder contents = new StringBuilder(); 

    public void log(String message) { 
     contents.append(Thread.currentThread().getName()); 
     // << getContents called at this point. 
     contents.append(message); 
     contents.append("\n"); 
    } 

    public String getContents() { 
     return contents.toString(); 
    } 
} 

的問題是現在很清楚 - 你必須確保在log正在執行,沒有其他線程可以調用getContents,因此,很顯然,這兩個必須同步。

public class Logger { 

    private StringBuilder contents = new StringBuilder(); 

    public synchronized void log(String message) { 
     contents.append(Thread.currentThread().getName()); 
     // << getContents called at this point. 
     contents.append(message); 
     contents.append("\n"); 
    } 

    public synchronized String getContents() { 
     return contents.toString(); 
    } 
} 

應該保證沒有部分日誌條目將永遠不會被getContents返回。

但是,仍然有一些漏洞可能會出現不一致(例如,一個線程看不到最近發佈的消息),但沒有一個會導致明顯的錯誤,例如格式錯誤的日誌條目。

很明顯:

public void test() { 
    Logger logger = new Logger(); 

    logger.log("Hello"); 
    String contents = logger.getContents(); 
    // Contents could easily contain "Hello" and more but this would not be consider an error. 
} 

可能會導致意想不到的結果,如你好入境後多日誌條目,但是這不會是一個錯誤。

順便說一句:你的日誌方法中沒有時間戳。

0

確實StringBuilder不是線程安全的。所以你不想讓多個線程調用這些方法。

但是,在方法簽名中使用​​關鍵字時,它不僅意味着2個線程不能同時訪問特定的方法。 關鍵字​​鎖定整個對象,而不僅僅是該特定的方法。

假設兩種方法都有​​關鍵字,如果一個線程將訪問getContents方法,則其他線程將不能同時訪問log方法。

PS:或者,您可以使用線程安全的StringBuffer。所以你根本不需要任何形式的鎖定。