如何在Java中使用同步 -Manusha

banq發表於2020-05-27

在本文中,您將學習為什麼我們需要Java同步,如何編寫同步的程式碼以及有關同步的更重要的要點。

為什麼要使用同步?
如果您的程式碼在多執行緒環境中執行,則需要同步在多個執行緒之間共享的物件。否則,可能會發生兩種型別的錯誤。

  1. 執行緒干擾錯誤
  2. 記憶體一致性錯誤

Java中的執行緒干擾是一種情況,當多個同時執行的執行緒可以訪問同一資料時,就會發生這種情況。當執行緒對同一資料執行不同的操作時,這些操作可能會重疊並在記憶體中建立不一致的資料。發生這種情況時,資料可能會丟失,損壞或顯示意外行為。
在多執行緒中,一個執行緒所做的更改可能對其他執行緒不可見,並且它們對同一共享資料的檢視不一致。這稱為記憶體一致性錯誤。
因此,我們在程式碼中使用了同步,以避免因這些錯誤而頭痛。您不需要或使用同步共享,如果物件是不可變的,如果所有的執行緒只能執行只讀操作。現在,讓我們看看如何同步我們的Java程式碼。

方法同步
您可以使用synchronized關鍵字在Java中宣告一個同步方法。當執行緒呼叫同步方法時,它會在共享資料時自動鎖定物件,並線上程完成其任務時釋放該物件。因此,如果一個執行緒執行同步方法,則在同一物件上呼叫同步方法的所有其他執行緒將必須等待,直到第一個執行緒完成。讓我們來看一個例子。

class Apple { 
    synchronized public void getApple() { 
        for (int i = 0; i < 3; i++) { 
            System.out.println(i); 
            try { 
                Thread.sleep(400); 
            } 
            catch (Exception e) { 
                System.out.println(e); 
            } 
        } 
    } 
} 
  
class Tree extends Thread { 
   
    Apple apple; 
  
    Tree (Apple apple) { 
        this.apple = apple; 
    } 
  
    @Override
    public void run() { 
        apple.getApple(); 
    } 
} 
  
public class SyncEx1 { 
    public static void main(String[] args) { 
     
        Apple obj = new Apple(); //Object of Apple class that is shared amoung threads
  
        Tree tree1 = new Tree(obj); 
        Tree tree2 = new Tree(obj); 
  
        tree1.start(); 
        tree2.start(); 
    } 
} 

getApple()方法在示例中是同步的,一次僅允許一個執行緒訪問該方法。因此,該變數的值i將不會不一致。但是,如果我們只想同步一段程式碼而不是整個方法,該怎麼辦?那就是我們需要塊同步的時候。

塊同步
假設您的方法中有50行程式碼,但是您只想同步5行。同步塊允許您同步方法的任何特定部分。它告訴JVM一次僅允許一個執行緒訪問指定的程式碼段。
類的synchronizedList()方法java.util.Collections用於返回由指定列表支援的同步列表。因此,每當Iterator使用進行迭代同步列表時,Iterator需要同步使用需求的程式碼塊。讓我們看一下示例程式碼。

import java.util.*;
public class SyncEx2{

    public static void main(String []args){
        try{
            List<String> al = new ArrayList<>(); 
            List movieList = Collections.synchronizedList(al);
            movieList.add("StarWars");
            movieList.add("Avengers");
            movieList.add("Inception");

            synchronized(movieList) {   // Synchronized block
              Iterator i = movieList.iterator(); 
              while (i.hasNext())
                System.out.println(i.next()); // Print Synchronized movie list
            }
            
            } catch (IllegalArgumentException e) { 
                System.out.println("Exception thrown : " + e); 
            } 
    } 
}


有關同步的重要說明
  • 如果同步靜態方法,則鎖將位於類上,而不是物件上。因此,無論它有多少物件例項,在這個類靜態同步方法中都只能執行一個執行緒。
  • 在同步方法或塊中可以使用3種執行緒間通訊方法。這些都是wait(),notify()和notifyAll()。
  • 您可以使用該wait()方法將當前執行緒設定為釋放鎖定,然後等待另一個執行緒呼叫該物件的一個notify()或多個notifyAll()方法,或者直到指定的時間過去。
  • notify()方法將選擇並喚醒在此物件上等待的單個執行緒,並且該notifyAll()方法將喚醒在該物件的監視器上等待的所有執行緒。
  • 同步有很多限制,它不允許併發訪問,可能會導致死鎖,並且同步方法的執行速度非常慢。
  • Java java.util.concurrent在Java 5中引入了該軟體包,它可以用作這些問題的解決方案。該軟體包提供了一組類,這些類使開發多執行緒Java應用程式更加容易。

相關文章