成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

到底什么是線程安全? 如何保證線程安全?

開發
我們通常會在代碼中使用多線程(比如線程池)來提高性能,但是,多線程又會帶來線程安全問題。因此,本文將深入探討Java中的線程安全問題。

隨著硬件技術的快速發展(比如多核處理器,超線程技術),我們通常會在代碼中使用多線程(比如線程池)來提高性能,但是,多線程又會帶來線程安全問題。因此,本文將深入探討Java中的線程安全問題。

1.什么是線程安全?

首先,我們來看看維基百科對線程安全是如何描述的,如下圖:

總結一下:線程安全(Thread Safety)是指多個線程訪問共享資源時,不會破壞資源的完整性。如下圖:

請注意,導致線程安全問題一定要同時具備以下 3個條件,缺一不可:

  • 多線程環境:如果是單線程,程序肯定會串行順序執行,不可能出現線程安全問題。
  • 操作共享資源:所謂共享資源是指多個線程或進程可以同時訪問和使用的資源。如果每個線程都是操作自己的局部變量,盡管滿足條件1,但也不會出現線程安全問題。
  • 至少存在一個寫操作:如果是多線程讀取共享資源,盡管滿足了前 2個條件,但是讀操作天然是冪等的,因此也不會出現線程安全的問題,所以線程中至少存在一個寫操作。

上面從表象上說明線程安全需要具備的 3個條件,在 Java中,線程安全性通常涉及以下 3個指標:

  • 原子性(Atomicity):操作要么全部完成,要么全部不完成。
  • 可見性(Visibility):一個線程對共享變量的修改對其他線程是立即可見的。
  • 有序性(Ordering):程序的執行順序符合預期,不會因為編譯器優化或CPU重排序而改變。

2. 產生線程安全的根因

在 Java中,造成線程安全問題的根因是硬件結構,為了消除 CPU和主內存之間的硬件速度差,通常會在兩者之間設置多級緩存(L1 ~ L3),如下圖:

Java為了適配這種多級緩存的硬件構造,設計了一套與之對應的內存模型(JMM,Java memory model,包括主內存和工作內存,如下圖:

  • 主內存:所有的變量都存儲在主內存中。
  • 工作內存:每個線程都有自己的工作內存,會將主內存的共享變量復制到自己的工作內存中,然后做后續業務操作,最終再將工作內存中的變量刷新到主內存。

線程對變量的所有操作(讀取、寫入)都必須在工作內存中進行,而不能直接讀寫主內存中的變量。線程之間無法直接訪問對方的工作內存,變量的傳遞需要通過主內存來完成。

關于 Java內存模型的原理,我們會在另外的文章中單獨講解,本文只是概要性的總結。

3. 原子性

在數據庫事務ACID中也有原子性(Atomicity)的概念,它是指一個操作是不可分割的,即要么全部執行,要么全部不執行。Java線程安全中的原子性與數據庫事務中的原子性本質是一樣的,只是它們應用的上下文和具體實現有所不同。

Java提供了多種方式來保證原子性,比如 同步塊、鎖或者原子類。

為了更好的說明原子性,我們這里以一個反例來展示不具備原子性,如下代碼:

public class AtomicityTest {
    private int i = 0;
    public void increment() {
        i++;
    }
}

在上述代碼中,i++這種寫法在我們的日常開發經常使用,但它不是一個原子操作,實際上i++分為三步:

  • 讀取i的值
  • 將i的值加 1
  • 將結果寫回給i

如果多個線程同時執行increment()方法,可能會導致i的值不正確,比如有 3個線程A,B,C:

  • 線程A讀取i的值,并且將i的值加 1,但是還未將結果寫回給i;
  • 此時,線程B讀取i的值仍然是0,并且將i的值加 1;
  • 線程A 將結果寫回給i,將i設置為 1;
  • 線程B 將結果寫回給i,將i設置為 1;
  • 線程C 讀取i的值為1,并且將i的值加 1,并且將結果寫回給i,將i設置為 2;

3個線程都對i進行i++操作,預期i的最終值是 3,但因為i++無法保證原子性,因此,i最終的值未達到預期的值。

4. 可見性

可見性是指一個線程對共享變量的修改,其他線程能立刻看到。在Java中,volatile關鍵字可以保證變量的可見性。

為了更好的說明可見性,我們這里以一個示例進行分析,如下代碼:

public class VisibilityTest {
    private boolean running = true;

    public void stop() {
        running = false;
    }

    public void run() {
        while (running) {
            // do something
        }
    }
}

在上述代碼中,變量running是一個全局變量,如果沒有使用volatile關鍵字,running 變量的修改可能不會被其他線程立即看到。

5. 有序性

有序性是指程序代碼的執行順序。在單線程環境中,代碼的執行順序通常是按照代碼的書寫順序執行的。然而,在多線程環境中,編譯器、JVM和CPU可能會為了優化性能進行指令重排序(Instruction Reordering),這可能會導致代碼的執行順序與預期不一致。

Java內存模型(Java Memory Model, JMM)允許編譯器和處理器進行指令重排序,但會保證單線程內的執行結果和多線程內的同步結果是正確的。

這里以一個反例來展示不具備有序性,如下代碼:

public class ReorderingExample {
private int x = 0;
private boolean flag = false;

    public void writer() {
        x = 42;
        flag = true;
    }

    public void read() {
        if (flag) {
            System.out.println(x); // 可能輸出0
        }
    }
}

在上述代碼中,read()方法可能會看到flag=true,但x仍然為 0,因為編譯器或CPU可能對指令進行重排序。

6. 如何保證線程安全

在 Java中,通常可以通過以下幾個方式來保證線程安全。

(1) synchronized關鍵字

synchronized是Java的一個原語關鍵字,它可以保證方法或代碼塊在同一時刻只能被一個線程執行,從而確保原子性和可見性。

下面的代碼是synchronized關鍵字的簡單使用:

public class SynchronizedTest {
private int i = 0;
    public synchronized void increment() {
        i++;
    }
    public synchronized int getCount() {
        return i;
    }
}

(2) Lock 接口

Lock接口提供了比synchronized更靈活的鎖機制,常用的實現類有 ReentrantLock 可重入鎖。

下面的代碼是Lock關鍵字的簡單使用:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockCounter {
private int count = 0;
private final Lock lock = new ReentrantLock();

    public void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }

    public int getCount() {
        lock.lock();
        try {
            return count;
        } finally {
            lock.unlock();
        }
    }
}

(3) 原子類

Java提供了一些原子類,如 AtomicInteger、AtomicLong 和 AtomicReference,它們通過CAS(Compare-And-Swap)操作實現了非阻塞的線程安全。

下面的代碼是AtomicInteger原子類的簡單使用:

import java.util.concurrent.atomic.AtomicInteger;

public class AtomicTest {
private AtomicInteger atomic = new AtomicInteger();

    public void increment() {
        atomic.incrementAndGet();
    }

    public int getCount() {
        return atomic.get();
    }
}

(4) ThreadLocal 類

ThreadLocal類提供了線程局部變量,每個線程都有自己獨立的變量副本,從而避免了共享數據的競爭。

下面的代碼是ThreadLocal類的簡單使用:

public class ThreadLocalExample {
private static final ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 1);

    public int getValue() {
        return threadLocal.get();
    }

    public void setValue(int value) {
        threadLocal.set(value);
    }
}

(5) 分布式鎖

Redis 分布式鎖 或者 Zookeeper分布式鎖是分布式環境下保證線程安全的常用方法。關于兩種分布式鎖的原理,會在其他的文章詳細分析。

7. 總結

線程安全是 Java多線程編程中很重要的一部分,本文講解了什么是線程安全以及產生線程安全問題的根因,并且通過原子性,有序性,可見性對線程安全進行了分析。

  • 硬件的多級緩存和Java與之對應的內存模型是導致線程安全的根因;
  • volatile可以保證變量的可見性,但不能保證原子性,因此無法保證線程安全;
  • synchronized,虛擬機鎖,原子類,分布式鎖可以保證線程的安全性;
責任編輯:趙寧寧 來源: 猿java
相關推薦

2023-01-26 02:07:51

HashSet線程安全

2024-06-17 00:02:00

線程安全HashMapJDK 1.7

2019-01-28 08:50:09

線程安全

2024-05-11 09:41:45

線程安全代碼

2020-12-28 08:18:55

安全代碼線程

2020-09-03 06:42:12

線程安全CPU

2017-07-06 15:36:56

線程線程安全開發

2022-06-07 23:28:05

線程安全后端

2022-09-26 13:46:18

Java線程安全

2019-07-29 10:10:06

Java內存線程安全

2012-04-16 10:12:54

Java線程

2022-01-24 07:01:20

安全多線程版本

2024-11-26 07:29:57

高并發線程安全

2021-06-30 13:31:18

線程安全ThreadLocal

2023-11-06 17:39:35

JavaArrayList線程

2020-06-12 10:03:01

線程安全多線程

2024-03-22 12:29:03

HashMap線程

2016-10-10 23:00:18

2024-06-04 08:32:40

2023-03-21 09:07:38

HashMap線程安全
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 日本欧美国产在线观看 | 久久精品国产亚洲夜色av网站 | 97精品国产97久久久久久免费 | 天天操狠狠操 | 成人免费视频7777777 | 亚洲三级在线观看 | 国产一区二| 亚洲巨乳自拍在线视频 | 欧美伊人久久久久久久久影院 | 亚洲视频在线观看 | 一级午夜aaa免费看三区 | 综合久久av | 日本视频一区二区三区 | 成人精品视频99在线观看免费 | 中文字幕在线三区 | 亚洲欧美日韩久久 | 日本一区二区三区视频在线 | 亚洲精品久久久一区二区三区 | 97人人澡人人爽91综合色 | 日韩精品视频一区二区三区 | 在线电影日韩 | 日韩成人av在线播放 | 国产精品一区二区视频 | 久久9精品 | 国产成人免费一区二区60岁 | 久久99精品国产 | 亚洲不卡视频 | av在线伊人 | 色资源在线视频 | 中文字幕视频在线 | 成人免费在线 | 欧美三级三级三级爽爽爽 | 国产精品久久久久久久久久久久 | 欧美日韩在线观看视频 | av天天爽 | 久久国| 亚洲福利在线视频 | 五月天婷婷综合 | 中文字幕一区二区三区不卡 | av在线三级| 精品九九 |