欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java線程安全問題的解決方案

 更新時間:2022年05月10日 10:14:33   作者:??Java中文社群????  
這篇文章主要介紹了Java線程安全問題的解決方案,文章關(guān)于安全的問題的解決方案本文主要分享了三種方法,使用線程安全類,比如?AtomicInteger,使用線程本地變量?ThreadLocal,加鎖排隊執(zhí)行,三種方法的使用場景及區(qū)別需要的小伙伴可以參考一下

前言:

線程安全是指某個方法或某段代碼,在多線程中能夠正確的執(zhí)行,不會出現(xiàn)數(shù)據(jù)不一致或數(shù)據(jù)污染的情況,我們把這樣的程序稱之為線程安全的,反之則為非線程安全的。在 Java 中,

解決線程安全問題有以下 3 種手段:

  • 使用線程安全類,比如 AtomicInteger。
  • 加鎖排隊執(zhí)行
    • 使用 synchronized 加鎖。
    • 使用 ReentrantLock 加鎖。
  • 使用線程本地變量 ThreadLocal。

接下來我們逐個來看它們的實現(xiàn)。

線程安全問題演示

我們創(chuàng)建一個變量 number 等于 0,之后創(chuàng)建線程 1,執(zhí)行 100 萬次 ++ 操作,同時再創(chuàng)建線程 2 執(zhí)行 100 萬次 -- 操作,等線程 1 和線程 2 都執(zhí)行完之后,打印 number 變量的值,如果打印的結(jié)果為 0,則說明是線程安全的,否則則為非線程安全的,

示例代碼如下:

public class ThreadSafeTest {
    // 全局變量
    private static int number = 0;
    // 循環(huán)次數(shù)(100W)
    private static final int COUNT = 1_000_000;

    public static void main(String[] args) throws InterruptedException {
        // 線程1:執(zhí)行 100W 次 ++ 操作
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < COUNT; i++) {
                number++;
            }
        });
        t1.start();

        // 線程2:執(zhí)行 100W 次 -- 操作
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < COUNT; i++) {
                number--;
            }
        });
        t2.start();

        // 等待線程 1 和線程 2,執(zhí)行完,打印 number 最終的結(jié)果
        t1.join();
        t2.join();
        System.out.println("number 最終結(jié)果:" + number);
    }
}

以上程序的執(zhí)行結(jié)果如下圖所示: 

 從上述執(zhí)行結(jié)果可以看出,number 變量最終的結(jié)果并不是 0,和預(yù)期的正確結(jié)果不相符,這就是多線程中的線程安全問題。

解決線程安全問題

1.原子類AtomicInteger

AtomicInteger 是線程安全的類,使用它可以將 ++ 操作和 -- 操作,變成一個原子性操作,這樣就能解決非線程安全的問題了,

如下代碼所示:

import java.util.concurrent.atomic.AtomicInteger;
public class AtomicIntegerExample {
    // 創(chuàng)建 AtomicInteger
    private static AtomicInteger number = new AtomicInteger(0);
    // 循環(huán)次數(shù)
    private static final int COUNT = 1_000_000;

    public static void main(String[] args) throws InterruptedException {
        // 線程1:執(zhí)行 100W 次 ++ 操作
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < COUNT; i++) {
                // ++ 操作
                number.incrementAndGet();
            }
        });
        t1.start();

        // 線程2:執(zhí)行 100W 次 -- 操作
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < COUNT; i++) {
                // -- 操作
                number.decrementAndGet();
            }
        });
        t2.start();

        // 等待線程 1 和線程 2,執(zhí)行完,打印 number 最終的結(jié)果
        t1.join();
        t2.join();
        System.out.println("最終結(jié)果:" + number.get());
    }
}

以上程序的執(zhí)行結(jié)果如下圖所示: 

2.加鎖排隊執(zhí)行

Java 中有兩種鎖:synchronized 同步鎖和 ReentrantLock 可重入鎖。

2.1 同步鎖synchronized

synchronized 是 JVM 層面實現(xiàn)的自動加鎖和自動釋放鎖的同步鎖,它的實現(xiàn)代碼如下:

public class SynchronizedExample {
    // 全局變量
    private static int number = 0;
    // 循環(huán)次數(shù)(100W)
    private static final int COUNT = 1_000_000;

    public static void main(String[] args) throws InterruptedException {
        // 線程1:執(zhí)行 100W 次 ++ 操作
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < COUNT; i++) {
                // 加鎖排隊執(zhí)行
                synchronized (SynchronizedExample.class) {
                    number++;
                }
            }
        });
        t1.start();

        // 線程2:執(zhí)行 100W 次 -- 操作
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < COUNT; i++) {
                // 加鎖排隊執(zhí)行
                synchronized (SynchronizedExample.class) {
                    number--;
                }
            }
        });
        t2.start();

        // 等待線程 1 和線程 2,執(zhí)行完,打印 number 最終的結(jié)果
        t1.join();
        t2.join();
        System.out.println("number 最終結(jié)果:" + number);
    }
}

以上程序的執(zhí)行結(jié)果如下圖所示: 

2.2 可重入鎖ReentrantLock

ReentrantLock 可重入鎖需要程序員自己加鎖和釋放鎖,它的實現(xiàn)代碼如下:

import java.util.concurrent.locks.ReentrantLock;

/**
 * 使用 ReentrantLock 解決非線程安全問題
 */
public class ReentrantLockExample {
    // 全局變量
    private static int number = 0;
    // 循環(huán)次數(shù)(100W)
    private static final int COUNT = 1_000_000;
    // 創(chuàng)建 ReentrantLock
    private static ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {
        // 線程1:執(zhí)行 100W 次 ++ 操作
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < COUNT; i++) {
                lock.lock();    // 手動加鎖
                number++;       // ++ 操作
                lock.unlock();  // 手動釋放鎖
            }
        });
        t1.start();

        // 線程2:執(zhí)行 100W 次 -- 操作
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < COUNT; i++) {
                lock.lock();    // 手動加鎖
                number--;       // -- 操作
                lock.unlock();  // 手動釋放鎖
            }
        });
        t2.start();

        // 等待線程 1 和線程 2,執(zhí)行完,打印 number 最終的結(jié)果
        t1.join();
        t2.join();
        System.out.println("number 最終結(jié)果:" + number);
    }
}

以上程序的執(zhí)行結(jié)果如下圖所示: 

3.線程本地變量ThreadLocal

使用 ThreadLocal 線程本地變量也可以解決線程安全問題,它是給每個線程獨自創(chuàng)建了一份屬于自己的私有變量,不同的線程操作的是不同的變量,所以也不會存在非線程安全的問題,它的實現(xiàn)代碼如下:

public class ThreadSafeExample {
    // 創(chuàng)建 ThreadLocal(設(shè)置每個線程中的初始值為 0)
    private static ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 0);
    // 全局變量
    private static int number = 0;
    // 循環(huán)次數(shù)(100W)
    private static final int COUNT = 1_000_000;

    public static void main(String[] args) throws InterruptedException {
        // 線程1:執(zhí)行 100W 次 ++ 操作
        Thread t1 = new Thread(() -> {
            try {
                for (int i = 0; i < COUNT; i++) {
                    // ++ 操作
                    threadLocal.set(threadLocal.get() + 1);
                }
                // 將 ThreadLocal 中的值進行累加
                number += threadLocal.get();
            } finally {
                threadLocal.remove(); // 清除資源,防止內(nèi)存溢出
            }
        });
        t1.start();

        // 線程2:執(zhí)行 100W 次 -- 操作
        Thread t2 = new Thread(() -> {
            try {
                for (int i = 0; i < COUNT; i++) {
                    // -- 操作
                    threadLocal.set(threadLocal.get() - 1);
                }
                // 將 ThreadLocal 中的值進行累加
                number += threadLocal.get();
            } finally {
                threadLocal.remove(); // 清除資源,防止內(nèi)存溢出
            }
        });
        t2.start();

        // 等待線程 1 和線程 2,執(zhí)行完,打印 number 最終的結(jié)果
        t1.join();
        t2.join();
        System.out.println("最終結(jié)果:" + number);
    }
}

以上程序的執(zhí)行結(jié)果如下圖所示: 

總結(jié)

在 Java 中,解決線程安全問題的手段有 3 種:1.使用線程安全的類,如 AtomicInteger 類;2.使用鎖 synchronized 或 ReentrantLock 加鎖排隊執(zhí)行;3.使用線程本地變量 ThreadLocal 來處理。

到此這篇關(guān)于Java線程安全問題的解決方案的文章就介紹到這了,更多相關(guān)Java線程安全內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot整合Spring Data Elasticsearch的過程詳解

    SpringBoot整合Spring Data Elasticsearch的過程詳解

    這篇文章主要介紹了SpringBoot整合Spring Data Elasticsearch的過程詳解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-09-09
  • Java調(diào)用JavaScript實現(xiàn)字符串計算器代碼示例

    Java調(diào)用JavaScript實現(xiàn)字符串計算器代碼示例

    這篇文章主要介紹了Java調(diào)用JavaScript實現(xiàn)字符串計算器代碼示例,具有一定參考價值,需要的朋友可以了解下。
    2017-12-12
  • java中Integer包裝類裝箱的一個細節(jié)詳解

    java中Integer包裝類裝箱的一個細節(jié)詳解

    Java中的Integer是int的包裝類型,下面這篇文章主要給大家介紹了關(guān)于java中Integer包裝類裝箱的一個細節(jié)的相關(guān)資料,文中介紹的這個細節(jié)挺重要的,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起看看吧
    2018-07-07
  • Java面向?qū)ο蠡A(chǔ),類,變量,方法

    Java面向?qū)ο蠡A(chǔ),類,變量,方法

    這篇文章主要介紹了Java面向?qū)ο蠡A(chǔ),類,變量,方法,需要的朋友可以參考下
    2020-10-10
  • Java編程讀寫鎖詳解

    Java編程讀寫鎖詳解

    本篇文章給大家詳細分享了Java編程讀寫鎖的相關(guān)原理以及知識點內(nèi)容,有興趣的朋友們可以參考下。
    2018-08-08
  • RocketMq事務(wù)消息發(fā)送代碼流程詳解

    RocketMq事務(wù)消息發(fā)送代碼流程詳解

    這篇文章主要介紹了RocketMq事務(wù)消息發(fā)送代碼流程詳解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-07-07
  • webuploader+springmvc實現(xiàn)圖片上傳功能

    webuploader+springmvc實現(xiàn)圖片上傳功能

    這篇文章主要為大家詳細介紹了webuploader+springmvc實現(xiàn)圖片上傳功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-09-09
  • 通過實例了解Java 8創(chuàng)建Stream流的5種方法

    通過實例了解Java 8創(chuàng)建Stream流的5種方法

    這篇文章主要介紹了通過實例了解Java 8創(chuàng)建Stream流的5種方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-12-12
  • Sentinel熱門詞匯限流的實現(xiàn)詳解

    Sentinel熱門詞匯限流的實現(xiàn)詳解

    這篇文章主要介紹了使用Sentinel對熱門詞匯進行限流的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • SpringBoot日志框架之Log4j2快速入門與參數(shù)詳解

    SpringBoot日志框架之Log4j2快速入門與參數(shù)詳解

    本文介紹了SpringBoot日志框架log4j2的基本使用和配置方法,包括將日志輸出到控制臺、文件、Elasticsearch和Kafka,多個輸出目的地的配置,異步日志記錄器的使用以及l(fā)og4j2.xml配置文件的詳細語法和參數(shù)含義,需要的朋友可以參考下
    2023-05-05

最新評論