JavaRush /Java Blog /Random-TW /線程同步。Java中的同步運算符

線程同步。Java中的同步運算符

在 Random-TW 群組發布
你好!今天我們繼續考慮多執行緒程式設計的特點,講講線程同步。
線程同步。 操作員已同步 - 1
什麼是“同步”?在程式設計領域之外,這指的是允許兩個裝置或程式一起工作的某種設定。例如,智慧型手機和電腦可以與Google帳戶同步,網站上的個人帳戶可以與社交網路上的帳戶同步,以便使用它們登入。線程同步具有類似的含義:它設定線程之間如何交互。在之前的講座中,我們的線程彼此分開生活和工作。一個在計數,第二個在睡覺,第三個在控制台上顯示一些東西,但他們之間沒有交互。在實際程序中,這種情況很少見。例如,多個執行緒可以主動處理同一組資料並更改其中的某些內容。這會產生問題。想像一下,多個執行緒正在將文字寫入同一位置,例如文字檔案或控制台。在這種情況下,該文件或控制台成為共享資源。執行緒不知道彼此的存在,因此它們只是在執行緒調度程序分配給它們的時間內寫下它們可以管理的所有內容。在課程最近的一次講座中,我們舉了一個例子來說明這會導致什麼結果,讓我們記住這一點: 線程同步。 同步操作員 - 2原因在於線程使用共享資源(控制台),而不相互協調它們的操作。如果執行緒調度程式已為 Thread-1 分配時間,它會立即將所有內容寫入控制台。其他執行緒已經成功寫入或尚未成功寫入的內容並不重要。正如您所看到的,結果是災難性的。因此,在多執行緒程式設計中,引入了一個特殊的概念互斥量(來自英文“mutex”,“互斥”——“互斥”)互斥體的目的是提供一種機制,以便在特定時間只有一個執行緒可以存取物件。如果 Thread-1 已取得物件 A 的互斥體,則其他執行緒將無法存取它來更改其中的任何內容。在物件 A 的互斥體被釋放之前,剩餘的線程將被迫等待。現實生活中的例子:假設您和其他 10 個陌生人正在參加訓練。你們需要輪流表達想法和討論某件事。但是,由於你們是第一次見面,為了不經常打斷對方,也不至於陷入喧鬧,你們採用了“說話球”規則:只有一個人可以說話——那個有球的人。他的手。這樣,討論就變得充分且富有成果。所以,互斥鎖本質上就是這樣一個球。如果一個物件的互斥鎖在一個執行緒手中,其他執行緒將無法存取該物件。您不需要執行任何操作來建立互斥體:它已經內建在類別中Object,這表示 Java 中的每個物件都擁有它。

Java 中同步運算子的工作原理

讓我們來熟悉一個新關鍵字——synchronized。它標記了我們程式碼的某一部分。如果一個程式碼區塊被標記為synchronized關鍵字,則表示該程式碼區塊一次只能由一個執行緒執行。同步可以透過不同的方式實現。例如,建立一個完整的同步方法:
public synchronized void doSomething() {

   //...method logic
}
或寫一段程式碼,在某個物件上執行同步:
public class Main {

   private Object obj = new Object();

   public void doSomething() {

       //...some logic available to all threads

       synchronized (obj) {

           //logic that is only available to one thread at a time
       }
   }
}
意思很簡單。如果一個線程進入一個標有“synchronized”一詞的程式碼區塊,它會立即獲取該物件的互斥量,並且嘗試進入同一塊或方法的所有其他線程都將被迫等待,直到前一個線程完成其工作並釋放該物件。監視器。 線程同步。 同步運算符 - 3順便一提!在課程講座中,您已經看到過同步的範例,但它們看起來有所不同:
public void swap()
{
   synchronized (this)
   {
       //...method logic
   }
}
這個主題對你來說是新的,當然一開始你會對文法感到困惑。所以,趕緊記住,以免以後寫法的時候出現混亂。這兩種寫法的意思是一樣的:
public void swap() {

   synchronized (this)
   {
       //...method logic
   }
}


public synchronized void swap() {

   }
}
在第一種情況下,您在進入方法後立即建立同步程式碼區塊。它由 object 同步this,即由目前物件同步。在第二個範例中,您將“synchronized”一詞放在整個方法上。不再需要明確指示對其執行同步的任何物件。一旦整個方法被標記了一個單詞,該方法將自動為該類別的所有物件同步。我們先不去深入討論哪種方法比較好。現在,選擇你最喜歡的:) 最重要的是要記住:只有當一個方法內部的所有邏輯同時由一個執行緒執行時,你才能宣告一個同步方法。例如,在這種情況下,doSomething()使方法同步將是一個錯誤:
public class Main {

   private Object obj = new Object();

   public void doSomething() {

       //...some logic available to all threads

       synchronized (obj) {

           //logic that is only available to one thread at a time
       }
   }
}
正如您所看到的,該方法的一部分包含不需要同步的邏輯。其中的程式碼可以由多個執行緒同時執行,並且所有關鍵位置都分配給單獨的同步區塊。一會兒。讓我們仔細看看講座中交換姓名的範例:
public void swap()
{
   synchronized (this)
   {
       //...method logic
   }
}
請注意:同步是使用 進行的this。也就是說,對於一個特定的物件MyClass。想像一下,我們有 2 個線程(Thread-1Thread-2)並且只有一個物件MyClass myClass。在這種情況下,如果Thread-1呼叫該方法myClass.swap(),則物件的互斥體將很忙,並且Thread-2當您嘗試呼叫它時,myClass.swap()它將掛起等待互斥體變為空閒。如果我們有 2 個線程和 2 個物件MyClass-myClass1並且myClass2- 在不同的物件上,我們的線程可以輕鬆地同時執行同步方法。第一個執行緒執行以下操作:
myClass1.swap();
第二個的作用是:
myClass2.swap();
在這種情況下,方法內的synchronized關鍵字swap()不會影響程式的運行,因為同步是針對特定物件進行的。在後一種情況下,我們有 2 個對象,因此,線程不會給彼此帶來問題。畢竟,兩個物件有2個不同的互斥體,它們的捕獲不相互依賴。

靜態方法中同步的特點

但是如果您需要同步靜態方法怎麼辦?
class MyClass {
   private static String name1 = "Olya";
   private static String name2 = "Lena";

   public static synchronized void swap() {
       String s = name1;
       name1 = name2;
       name2 = s;
   }

}
目前尚不清楚在這種情況下什麼將充當互斥體。畢竟,我們已經決定每個物件都有一個互斥體。但問題是,要呼叫靜態方法,MyClass.swap()我們不需要物件:方法是靜態的!那麼,下一步是什麼?:/ 其實這個沒有問題。Java 的創建者照顧了一切:) 如果包含關鍵「多執行緒」邏輯的方法是靜態的,同步將由類別執行。為了更清楚起見,上面的程式碼可以重寫為:
class MyClass {
   private static String name1 = "Olya";
   private static String name2 = "Lena";

   public static void swap() {

       synchronized (MyClass.class) {
           String s = name1;
           name1 = name2;
           name2 = s;
       }
   }

}
原則上,您可以自己想到這一點:由於沒有對象,因此同步機制必須以某種方式「硬連線」到類別本身。就是這樣:您還可以跨類別同步。
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION