国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

實現線程安全的11種方法,你學會了嗎?

開發 前端
盡管多線程是一個強大的特性,但它也有代價。在多線程環境中,我們需要時刻崩著線程安全這根弦,即在不同的線程可以訪問相同的資源,而不會暴露錯誤行為或產生不可預測的結果,這種編程方法被稱為“線程安全”。

Java原生支持多線程,意味著通過在獨立的線程中并發運行,JVM能夠提升應用程序的性能。

盡管多線程是一個強大的特性,但它也有代價。在多線程環境中,我們需要時刻崩著線程安全這根弦,即在不同的線程可以訪問相同的資源,而不會暴露錯誤行為或產生不可預測的結果,這種編程方法被稱為“線程安全”。

一、無狀態實現

在大多數情況下,多線程中的錯誤是由于多個線程之間錯誤地共享狀態導致的。

因此,我們首先要探討的方法是:使用無狀態實現來達到線程安全。

為了更好地理解這種方法,先創建一個簡單的工具類,它有一個靜態方法用于計算數字的階乘:

public class MathUtils {

    public static BigInteger factorial(int number) {
        BigInteger f = new BigInteger("1");
        for (int i = 2; i <= number; i++) {
            f = f.multiply(BigInteger.valueOf(i));
        }
        return f;
    }
}

factorial()方法是一個無狀態的確定性函數:給定特定的輸入,總是得到相同的輸出。

該方法既不依賴外部狀態,也不維護狀態。因此,它被認為是線程安全的,可以同時被多個線程安全地調用。

所有線程都可以安全地調用factorial()方法,并將獲得預期的結果,而不會相互干擾,也不會改變該方法為其他線程生成的輸出。

因此,無狀態實現是實現線程安全的最簡單方法。

二、不可變實現

如果我們需要在不同線程之間共享狀態,我們可以通過使類不可變來創建線程安全的類。

不可變性是一個強大的、與語言無關的概念,在Java中很容易實現。在函數式編程中,很重要的一個技巧就是不可變,參見什么是函數式編程?。

簡單地說,當一個類實例在構造后其內部狀態不能被修改時,它就是不可變的。

在Java中創建不可變類的最簡單方法是聲明所有字段為私有且為final,并且不提供設置器:

public class MessageService {

    privatefinal String message;

    public MessageService(String message) {
        this.message = message;
    }

    public String getAndPrint() {
        System.out.println(message);
        return message;
    }

    public String getMessage() {
        return message;
    }
}

一個MessageService對象,在其構造后其狀態不能改變,所以是線程安全的。

此外,如果MessageService是可變的,但多個線程對其只有只讀權限,它也是線程安全的。

如我們所見,不可變性是實現線程安全的另一種方式。

三、線程局部字段

在面向對象編程(OOP)中,對象實際上需要通過字段維護狀態,并通過一個或多個方法實現行為。

如果我們確實需要維護狀態,我們可以使用線程局部字段來創建線程安全的類,線程局部字段在線程之間就不共享狀態。

我們可以通過在Thread類中定義私有字段輕松創建字段為線程局部的類。

比如,我們可以定義一個Thread類,它存儲一個整數數組:

public class ThreadA extends Thread {

    private final List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);

    @Override
    public void run() {
        numbers.forEach(System.out::println);
    }
}

另一個Thread類可能持有一個字符串數組:

public class ThreadB extends Thread {
    
    private final List<String> letters = Arrays.asList("a", "b", "c", "d", "e", "f");
    
    @Override
    public void run() {
        letters.forEach(System.out::println);
    }
}

在這兩種實現中,類都有自己的狀態,但不與其他線程共享。因此,這些類是線程安全的。

類似地,我們可以通過將ThreadLocal實例分配給一個字段來創建線程局部字段。

考慮以下StateHolder類:

public class StateHolder {
    private String state;

    public StateHolder(String state) {
        this.state = state;
    }

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }
}

我們可以很容易地使其成為一個線程局部變量:

public class ThreadState {

    public static final ThreadLocal<StateHolder> statePerThread =
            ThreadLocal.withInitial(() -> new StateHolder("active"));

    public static StateHolder getState() {
        return statePerThread.get();
    }
}

線程局部字段與普通類字段非常相似,不同之處在于每個通過setter/getter訪問它們的線程,都會獲得該字段的獨立初始化副本,以便每個線程都有自己的狀態。

四、同步集合

我們可以通過使用集合框架中包含的同步包裝器輕松創建線程安全的集合。

比如,我們可以創建一個線程安全的集合:

Collection<Integer> syncCollection = Collections.synchronizedCollection(new ArrayList<>());
Thread thread1 = new Thread(() -> syncCollection.addAll(Arrays.asList(1, 2, 3, 4, 5, 6)));
Thread thread2 = new Thread(() -> syncCollection.addAll(Arrays.asList(7, 8, 9, 10, 11, 12)));
thread1.start();
thread2.start();

請記住,同步集合在每個方法中使用內部鎖,這意味著這些方法一次只能被一個線程訪問,而其他線程將被阻塞,直到第一個線程釋放該方法的鎖。

五、并發集合

作為同步集合的替代方案,我們可以使用并發集合來創建線程安全的集合。

Java提供了java.util.concurrent包,其中包含幾個并發集合,比如ConcurrentHashMap:

Map<String,String> concurrentMap = new ConcurrentHashMap<>();
concurrentMap.put("1", "one");
concurrentMap.put("2", "two");
concurrentMap.put("3", "three");

與同步集合不同,并發集合通過將數據分割成段來實現線程安全。例如,在ConcurrentHashMap中,多個線程可以獲取不同映射段的鎖,因此多個線程可以同時訪問該映射。

由于并發線程訪問的固有優勢,并發集合比同步集合性能更高。

需要注意的是,無論同步集合還是并發集合,都是集合本身線程安全,其內容并不是。

六、原子對象

我們還可以使用Java提供的原子類集合來實現線程安全,包括AtomicInteger、AtomicLong、AtomicBoolean和AtomicReference。

原子類允許我們執行原子操作,這些操作是線程安全的,而無需使用同步。

為了理解這解決了什么問題,讓我們看一下以下Counter類:

public class Counter {

    private int counter = 0;

    public void incrementCounter() {
        counter += 1;
    }

    public int getCounter() {
        return counter;
    }
}

假設在一個競爭條件下,兩個線程同時訪問incrementCounter()方法。

理論上,counter字段的最終值將為2。但我們不能確定結果,因為線程同時執行相同的代碼塊,并且遞增操作不是原子的。可以參見在多線程中使用ArrayList會發生什么?的說明。

讓我們使用AtomicInteger對象創建Counter類的線程安全實現:

public class AtomicCounter {

    private final AtomicInteger counter = new AtomicInteger();

    public void incrementCounter() {
        counter.incrementAndGet();
    }

    public int getCounter() {
        return counter.get();
    }
}

這是線程安全的,因為雖然遞增操作++需要多個操作,但incrementAndGet是原子的。

七、同步方法

前面的方法對于集合和基本類型非常有用,但有時我們需要更復雜的控制邏輯。

因此,我們可以使用的另一種常見方法是:實現同步方法來實現線程安全。

簡單地說,一次只能有一個線程訪問同步方法,同時阻止其他線程訪問該方法。其他線程將保持阻塞,直到第一個線程完成或該方法拋出異常。

我們可以通過將incrementCounter()方法變為同步方法來以另一種方式創建其線程安全版本:

public synchronized void incrementCounter() {
    counter += 1;
}

我們通過在方法簽名前加上synchronized關鍵字創建了一個同步方法。

由于一次只能有一個線程訪問同步方法,一個線程將執行incrementCounter()方法,其他線程將依次執行。不會發生任何重疊執行。

同步方法依賴于使用“內部鎖”或“監視器”,內部鎖是與特定類實例相關聯的隱式內部實體。具體參加synchronized 鎖同步。

在多線程上下文中,“監視器”只是對鎖在相關對象上執行的角色的引用,它強制對一組指定的方法或語句進行獨占訪問。

當一個線程調用同步方法時,它獲取內部鎖,線程執行完方法后,會釋放鎖,允許其他線程獲取鎖并訪問該方法。

我們可以在實例方法、靜態方法和語句(同步語句)中實現同步。

八、同步語句

有時,如果我們只需要使方法的一部分線程安全,同步整個方法可能有些過度。

我們再重構incrementCounter()方法:

public void incrementCounter() {
    // 其他未同步的操作
    synchronized(this) {
        counter += 1; 
    }
}

假設該方法現在執行一些其他不需要同步的操作,我們通過將相關的狀態修改部分包裝在同步塊中來僅同步這部分。

與同步方法不同,同步語句必須指定提供內部鎖的對象,通常是this引用。

同步是有代價的,通過同步代碼塊,能夠僅同步方法的相關部分。

(一)其他對象作為鎖

我們可以通過利用另一個對象作為監視器鎖(而不是this)來稍微改進Counter類的線程安全實現。

這不僅在多線程環境中為共享資源提供了協調訪問,還使用外部實體來強制對資源的獨占訪問:

public class ObjectLockCounter {

    privateint counter = 0;
    privatefinal Object lock = new Object();

    public void incrementCounter() {
        synchronized (lock) {
            counter += 1;
        }
    }

    public int getCounter() {
        return counter;
    }
}

我們使用一個普通的Object實例來實現互斥,它提高了鎖級別的安全性。

當使用this進行內部鎖時,攻擊者可以通過獲取內部鎖并觸發拒絕服務(DoS)條件來導致死鎖。

相反,當使用其他對象時,無法從外部訪問這個私有對象,攻擊者很難獲取鎖并導致死鎖。

(二)注意事項

盡管我們可以使用任何Java對象作為內部鎖,但我們應該避免使用String進行鎖定:

public class Class1 {
    privatestaticfinal String LOCK  = "Lock";

    // 使用LOCK作為內部鎖
}

publicclass Class2 {
    privatestaticfinal String LOCK  = "Lock";

    // 使用LOCK作為內部鎖
}

乍一看,似乎這兩個類使用了兩個不同的對象作為它們的鎖。然而,由于字符串駐留,這兩個“Lock”值實際上可能在字符串池中引用同一個對象。也就是說,Class1和Class2共享同一個鎖!

除了String,我們應該避免使用任何可緩存或可重用的對象作為內部鎖。比如,Integer.valueOf()方法緩存小數字。因此,即使在不同的類中調用Integer.valueOf(1)也會返回同一個對象。

九、易失性字段

同步方法和塊對于解決線程之間的變量可見性問題很方便,即便如此,常規類字段的值可能會被CPU緩存。因此,即使對特定字段進行了同步更新,其他線程可能也看不到這些更新。

為了防止這種情況,我們可以使用易失性類字段(通過volatile關鍵字標記):

public class Counter {

    private volatile int counter;

    // 標準的構造函數/獲取器
}

通過使用volatile關鍵字,我們指示JVM和編譯器將counter變量存儲在主內存中。這樣,我們確保每次JVM讀取counter變量的值時,它實際上是從主內存中讀取,而不是從CPU緩存中讀取。同樣,每次JVM寫入counter變量時,值將被寫入主內存。

此外,使用易失性變量確保給定線程可見的所有變量也將從主內存中讀取。

比如:

public class User {

    private String name;
    private volatile int age;

    // 標準的構造函數/獲取器
}

在這種情況下,每次JVM將age易失性變量寫入主內存時,它也會將非易失性name變量寫入主內存。這確保了兩個變量的最新值都存儲在主內存中,因此對變量的后續更新將自動對其他線程可見。

類似地,如果一個線程讀取易失性變量的值,該線程可見的所有變量也將從主內存中讀取。

易失性變量提供的這種擴展保證被稱為完全易失性可見性保證。

十、可重入鎖

Java提供了一組改進的鎖實現,其行為比上面討論的內部鎖稍微復雜一些。

對于內部鎖,鎖獲取模型相當嚴格:一個線程獲取鎖,然后執行一個方法或代碼塊,最后釋放鎖,以便其他線程可以獲取它并訪問該方法。內部鎖沒有實現檢查排隊的線程并優先訪問等待時間最長的線程,即屬于非公平鎖。

ReentrantLock實例允許我們做到這一點,防止排隊的線程遭受某些類型的資源饑餓:

public class ReentrantLockCounter {

    privateint counter;
    privatefinal ReentrantLock reLock = new ReentrantLock(true);

    public void incrementCounter() {
        reLock.lock();
        try {
            counter += 1;
        } finally {
            reLock.unlock();
        }
    }

    public int getCounter() {
        return counter;
    }
}

ReentrantLock構造函數接受一個可選的公平性布爾參數,當設置為true且多個線程試圖獲取鎖時,JVM將優先考慮等待時間最長的線程并授予其訪問鎖的權限,即實現公平鎖。

十一、讀寫鎖

我們可以使用讀寫鎖實現來實現線程安全。讀寫鎖實際上使用一對相關聯的鎖,一個用于只讀操作,另一個用于寫入操作。

因此,只要沒有線程正在寫入資源,就可以有多個線程讀取該資源。此外,寫入資源的線程將阻止其他線程讀取它。

以下是我們如何使用讀寫鎖:

public class ReentrantReadWriteLockCounter {

    privateint counter;
    privatefinal ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
    privatefinal Lock readLock = rwLock.readLock();
    privatefinal Lock writeLock = rwLock.writeLock();

    public void incrementCounter() {
        writeLock.lock();
        try {
            counter += 1;
        } finally {
            writeLock.unlock();
        }
    }

    public int getCounter() {
        readLock.lock();
        try {
            return counter;
        } finally {
            readLock.unlock();
        }
    }
}

文末總結

在本文中,我們了解了Java中的線程安全是什么,并深入研究了實現線程安全的11種方法。

責任編輯:武曉燕 來源: 看山的小屋
相關推薦

2024-01-10 07:38:08

2023-10-30 11:40:36

OOM線程池單線程

2023-01-28 09:50:17

java多線程代碼

2022-06-16 07:50:35

數據結構鏈表

2024-10-06 08:20:53

鎖定機制編程

2023-12-11 08:03:01

Java線程線程組

2024-07-29 10:35:44

KubernetesCSI存儲

2022-09-22 12:03:14

網絡安全事件

2022-07-08 09:27:48

CSSIFC模型

2024-01-19 08:25:38

死鎖Java通信

2023-01-10 08:43:15

定義DDD架構

2023-07-26 13:11:21

ChatGPT平臺工具

2024-02-04 00:00:00

Effect數據組件

2024-03-12 08:37:32

asyncawaitJavaScript

2023-12-07 12:29:49

Nginx負載均衡策略

2023-12-07 07:03:09

2024-11-11 00:00:00

getHTML()DOM結構

2022-07-26 00:25:57

PandasQuery索引器

2024-11-28 10:32:32

2025-09-03 04:11:00

點贊
收藏

51CTO技術棧公眾號

激情欧美一区二区三区在线观看| 亚洲高清不卡一区| 日韩一区二区av| 亚洲狠狠婷婷综合久久久| 99色在线观看| 亚洲男女自偷自拍| 欧美日韩卡一卡二| 欧美日韩视频在线一区二区观看视频| 精品无人乱码| 国产无人区一区二区三区| 成人免费看片视频在线观看| 国产精品一区二区三区免费| 欧美中文在线视频| 性史性dvd影片农村毛片| 一区二区影院| 国产亚洲精品高潮| 激情视频综合网| 欧美丝袜丝交足nylons172| 国产精品久久久精品| 一区二区欧美视频| 先锋影音资源999| 精品国产一区二区三区久久狼黑人 | 亚洲福利视频导航| 日本中文字幕亚洲| 亚洲人成网www| 久久精品国产欧美激情| 在线看日韩av| 欧美一区二区三区四区五区六区| 欧美一区二区精品久久911| 国产高清在线不卡| www.四虎成人| 日韩二区三区四区| 性做久久久久久久免费看| 精品99999| 先锋影音亚洲资源| 国产视频在线看| 国产精品久久免费视频| 国产清纯白嫩初高生在线观看91 | 天堂av最新在线| 亚洲激情自拍视频| 日韩少妇内射免费播放| 欧美精品一区二区三区中文字幕 | 99久久精品免费看国产四区| 国产精品美女久久久| 公共露出暴露狂另类av| 国产一本一道久久香蕉| 污污动漫在线观看| 欧美午夜视频在线观看| 青草av在线| 亚洲精品国产精品自产a区红杏吧 亚洲精品国产精品乱码不99按摩 亚洲精品国产精品久久清纯直播 亚洲精品国产精品国自产在线 | 亚洲精品日日夜夜| 日韩黄色视屏| 国产精品的网站| av黄色在线网站| 99久久婷婷国产| 福利在线小视频| 亚洲欧洲美洲综合色网| 免费观看黄色大片| 亚洲国产精品自拍| 尤物视频免费在线观看| 亚洲香蕉伊综合在人在线视看 | 欧美日韩国产小视频在线观看| 三级无遮挡在线观看| 日韩一级在线观看| 亚洲毛片在线免费| 国产精品视频999| 99精品视频一区二区三区| 看黄网站在线| 国产一区二区三区视频在线观看 | 色欧美片视频在线观看| 久久综合五月婷婷| 成人黄色大片网站| 欧美性色黄大片| 欧美在线色图| av美女在线| 精品粉嫩超白一线天av| 亚洲精品韩国| 四虎影视2018在线播放alocalhost | www.成人| 91福利国产精品| 国产精品亲子伦av一区二区三区| 精品久久久91| 成人app下载| caoporn免费在线视频| 国产精华一区| 欧美日韩激情一区二区三区| 激情视频极品美女日韩| 99视频在线视频| 5566成人精品视频免费| 欧美国产精品一区二区三区| 高清精品在线| 国内偷自视频区视频综合| 国产精品亚洲视频| 免费在线成人激情电影| 香港三级日本三级a视频| 久久亚洲成人精品| 成人禁用看黄a在线| 国产福利电影在线播放| 国产高清在线不卡| 欧美极品另类videosde| 综合伊思人在钱三区| 久久精品国产亚洲a∨麻豆| 久久久久免费网| 亚洲欧美激情视频| 国产成人高清视频| 91麻豆精品激情在线观看最新| av在线无限看| 96国产粉嫩美女| 欧美成人精品高清在线播放 | 国产亚洲电影| 毛片在线免费| 大陆极品少妇内射aaaaa| 日韩国产中文字幕| 久久精品在线观看| 亚洲国产欧美日韩在线观看第一区 | 大香伊人久久精品一区二区| 中文在线中文字幕| 鲁鲁视频www一区二区| 日韩欧美在线视频| 久久久久久久高潮| 免费毛片在线不卡| 极品在线视频| 二区三区在线| 免费在线成人av电影| 久久精品中文字幕电影| 婷婷综合在线观看| 国产v综合v亚洲欧| 黄色亚洲大片免费在线观看| 草莓视频成人appios| av香蕉成人| 春日野结衣av| 成人高h视频在线| 韩国福利视频一区| 欧美日韩精品是欧美日韩精品| 亚洲男同性视频| 国产91精品久久久久久久网曝门 | 日韩免费视频播放| 中文字幕国产精品| 一区二区在线免费观看| 久久在线视频免费观看| 二区三区在线观看| 青青草精品视频在线观看| 日韩有码视频在线| 日韩一级黄色大片| 色综合网站在线| 欧美性xxxxx极品少妇| 国产成人综合亚洲网站| 蜜臀久久99精品久久久画质超高清 | 日韩欧美午夜| av资源中文在线天堂| 日韩一区国产在线观看| 在线电影av不卡网址| 日韩欧美亚洲一二三区| 日韩成人在线电影网| 欧美亚洲免费在线一区| 久久综合久久鬼色中文字| 久久婷婷影院| 欧美日韩精品一本二本三本| 亚洲男人都懂第一日本| 国产精品99久久免费观看| 99热播精品免费| 日韩av黄色| 青青在线精品| 厕沟全景美女厕沟精品| 国产三级视频在线看| 中文字幕电影在线观看| 欧美黄色免费网址| 日韩欧美xxxx| 久久精品a一级国产免视看成人 | 久久夜夜久久| 色姑娘综合网| 欧美精品tushy高清| 久久av中文| 中文在线免费| 日本最新在线视频| 欧美黑人疯狂性受xxxxx野外| 玖玖玖电影综合影院| 欧美视频亚洲视频| 国产suv精品一区二区883| 国产精品私人影院| 欧美亚洲一区三区| 亚洲第一网中文字幕| 欧美激情久久久久| 国产男女猛烈无遮挡91| 青少年xxxxx性开放hg| 免费av网页| 在线能看的av网址| 成人影院大全| 亚洲制服一区| 国产在线视频不卡二| 一本色道久久综合精品竹菊| 在线成人中文字幕| 国产精品一香蕉国产线看观看| 经典三级在线视频| 欧美成人综合在线| 免费一级欧美片在线观看网站| 蜜桃精品视频在线| 久久午夜羞羞影院免费观看| 色美美综合视频| 2019国产精品视频|