亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java線程安全問(wèn)題的解決方案

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

前言:

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

解決線程安全問(wèn)題有以下 3 種手段:

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

接下來(lái)我們逐個(gè)來(lái)看它們的實(shí)現(xiàn)。

線程安全問(wèn)題演示

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

示例代碼如下:

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é)果不相符,這就是多線程中的線程安全問(wèn)題。

解決線程安全問(wèn)題

1.原子類(lèi)AtomicInteger

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

如下代碼所示:

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.加鎖排隊(duì)執(zhí)行

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

2.1 同步鎖synchronized

synchronized 是 JVM 層面實(shí)現(xiàn)的自動(dòng)加鎖和自動(dòng)釋放鎖的同步鎖,它的實(shí)現(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++) {
                // 加鎖排隊(duì)執(zhí)行
                synchronized (SynchronizedExample.class) {
                    number++;
                }
            }
        });
        t1.start();

        // 線程2:執(zhí)行 100W 次 -- 操作
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < COUNT; i++) {
                // 加鎖排隊(duì)執(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 可重入鎖需要程序員自己加鎖和釋放鎖,它的實(shí)現(xiàn)代碼如下:

import java.util.concurrent.locks.ReentrantLock;

/**
 * 使用 ReentrantLock 解決非線程安全問(wèn)題
 */
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();    // 手動(dòng)加鎖
                number++;       // ++ 操作
                lock.unlock();  // 手動(dòng)釋放鎖
            }
        });
        t1.start();

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

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

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

3.線程本地變量ThreadLocal

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

public class ThreadSafeExample {
    // 創(chuàng)建 ThreadLocal(設(shè)置每個(gè)線程中的初始值為 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 中的值進(jìn)行累加
                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 中的值進(jìn)行累加
                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 中,解決線程安全問(wèn)題的手段有 3 種:1.使用線程安全的類(lèi),如 AtomicInteger 類(lèi);2.使用鎖 synchronized 或 ReentrantLock 加鎖排隊(duì)執(zhí)行;3.使用線程本地變量 ThreadLocal 來(lái)處理。

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

相關(guān)文章

  • SpringBoot整合Spring Data Elasticsearch的過(guò)程詳解

    SpringBoot整合Spring Data Elasticsearch的過(guò)程詳解

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

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

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

    java中Integer包裝類(lèi)裝箱的一個(gè)細(xì)節(jié)詳解

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

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

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

    Java編程讀寫(xiě)鎖詳解

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

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

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

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

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

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

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

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

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

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

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

最新評(píng)論