簡體   English   中英

方法何時應聲明為同步?

[英]When should a method be declared synchronized?

以下是一個簡單的工作程序,該程序使用兩個線程來打印計數器:

public class SynchronizedCounter implements Runnable {

    private static int i = 0;

    public void increment() { i++; }
    public int getValue() { return i; }  

    @Override
    public void run() {
        for( int i = 0; i < 5; i++ ) {
            synchronized( this ) {
                increment();
                System.out.println( Thread.currentThread().getName() + " counter: " + this.getValue() );
            }
        }
    }

    public static void main( String[] args ) {

        ExecutorService executorService = Executors.newFixedThreadPool( 2 );
        SynchronizedCounter synchronizedCounter = new SynchronizedCounter();
        executorService.submit( synchronizedCounter );
        executorService.submit( synchronizedCounter );  
        executorService.shutdown();

    }

}

輸出是預期的-兩個線程按順序顯示計數器。

如果將increment()getValue()聲明為已synchronized並且已注釋synchronized塊代碼,則輸出將顯示可見性和競爭問題。

由於未實現increment()getValue()的聲明為已synchronized並且僅通過使用synchronized塊(通過監視對象)才能實現同步,因此在什么情況下應將它們聲明為已synchronized

當需要在方法主體周圍使用同步塊的語義時,聲明一個方法已同步。

synchronized void increment() {
  ...
}

與以下內容完全相同:

void increment() {
  synchronized (this) { ... }
}

在上面的代碼中這樣做的事情是,您不再原子地執行increment()getValue()方法:另一個線程可以插入並在線程的調用之間執行這些方法:

Thread 1             Thread 2

increment()
                     increment()
                     getValue()
getValue()

這是可能的,但是兩個線程不能同時執行increment() (或getValue() ),因為它們是同步的(*)。

另一方面,像問題代碼中那樣圍繞調用進行同步意味着這兩個調用是原子執行的,因此這兩個線程不能交織:

Thread 1             Thread 2

increment()
getValue()
                     increment()
                     getValue()

(*)實際上,他們都可以同時執行該方法。 只是,除了一個線程外,所有線程都將在synchronized (this)等待。

在您的代碼中,類本身不是線程安全的,它取決於客戶端(您的run方法)以確保線程安全。

如果要使類線程安全,以使客戶端不需要執行任何特定操作即可使用該類線程,則它應具有適當的同步機制,並提出“更高級別”的方法,必要時可以在組合操作上運行原子操作。

例如,考慮一下AtomicInteger ,它具有一個incrementAndGet()方法,該方法將提供更好的方法。 您的run方法將變為:

for( int i = 0; i < 5; i++ ) {
  int newValue = counter.incrementAndGet();
  System.out.println(Thread.currentThread().getName() + " counter: " + newValue );
}

您的increment()方法使用的是i++ ,實際上它已被3條指令代替。 如果該方法未聲明為已synchronized則多個線程可能會同時執行該方法,這將導致爭用條件和錯誤結果。

如果將gain()和getValue()聲明為已同步,並且已注釋同步塊代碼,則輸出將顯示可見性和競爭問題。

在這種情況下,沒有比賽條件。 您會看到不同的結果,因為現在不是原子地執行crement increment()getValue() (就原子而言,我是指兩個方法作為一個同步塊),而一個線程稱為increment()並即將調用getValue()另一個線程也稱為increment()

因此,如果您打算在run方法的synchronized塊之外調用它們,則必須使increment()getValue()同步(並且由於它們被聲明為public ,因此有可能進行此類調用)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM