這8種保證線程安全的技術你都知道嗎?

併發狀況下如何保證數據安全,一直都是開發人員天天都要面對的問題,稍不注意就會出現數據異常,形成不可挽回的結果。筆者根據本身的實際開發經驗,總結了下面幾種保證數據安全的技術手段:安全

  1. 無狀態
  2. 不可變
  3. 安全的發佈
  4. volatile
  5. synchronized
  6. lock
  7. cas
  8. threadlocal

一.無狀態

咱們都知道只有多個線程訪問公共資源的時候,纔可能出現數據安全問題,那麼若是咱們沒有公共資源,是否是就沒有這個問題呢?多線程

public class NoStatusService {

    public void add(String status) {
        System.out.println("add status:" + status);
    }

    public void update(String status) {
        System.out.println("update status:" + status);
    }
}

二.不可變

若是多個線程訪問公共資源是不可變的,也不會出現數據的安全性問題。併發

public class NoChangeService {

    public static final String DEFAULT_NAME = "abc";

    public void add(String status) {
        System.out.println("add status:" + status);
    }
}

三.安全的發佈

若是類中有公共資源,可是沒有對外開放訪問權限,即對外安全發佈,也沒有線程安全問題性能

public class SafePublishService {

    private String name;

    public String getName() {
        return name;
    }

    public void add(String status) {
        System.out.println("add status:" + status);
    }
}

四.volatile

若是有些公共資源只是一個開關,只要求可見性,不要求原子性,這樣能夠用volidate關鍵字定義來解決問題。this

public class FlagService {

    public volatile boolean flag = false;


    public void change() {
        if (flag) {
            System.out.println("return");
            return;
        }
        flag = true;
        System.out.println("change");
    }
}

五.synchronized

使用JDK內部提供的同步機制,這也是使用比較多的手段,分爲:方法同步 和 代碼塊同步,咱們優先使用代碼塊同步,由於方法同步的範圍更大,更消耗性能。每一個對象內部都又一把鎖,只有搶答那把鎖的線程,才能進入代碼塊裏,代碼塊執行完以後,會自動釋放鎖。編碼

public class SyncService {

    private int age = 1;

    public synchronized void add(int i) {
        age = age + i;
        System.out.println("age:" + age);
    }

    public void update(int i) {
        synchronized (this) {
            age = age + i;
            System.out.println("age:" + age);
        }
    }
}

六.lock

除了使用synchronized關鍵字實現同步功能以外,JDK還提供了lock顯示鎖的方式。它包含:可重入鎖、讀寫鎖 等更多更強大的功能,有個小問題就是須要手動釋放鎖,不過在編碼時提供了更多的靈活性。atom

public class LockService {
    private ReentrantLock reentrantLock = new ReentrantLock();

    public int age = 1;


    public void add(int i) {
        try {
            reentrantLock.lock();
            age = age + i;
            System.out.println("age:" + age);
        } finally {
            reentrantLock.unlock();
        }
    }
}

七.cas

JDK除了使用鎖的機制解決多線程狀況下數據安全問題以外,還提供了cas機制。這種機制是使用CPU中比較和交換指令的原子性,JDK裏面是經過Unsafe類實現的。cas須要四個值:舊數據、指望數據、新數據 和 地址,比較舊數據 和 指望的數據若是同樣的話,就把舊數據改爲新數據,當前線程不斷自旋,一直到成功爲止。不過可能會出現aba問題,須要使用AtomicStampedReference增長版本號解決。其實,實際工做中不多直接使用Unsafe類的,通常用atomic包下面的類便可。線程

public class AtomicService {

    private AtomicInteger atomicInteger = new AtomicInteger();


    public int add(int i) {
        return atomicInteger.getAndAdd(i);
    }
}

八.threadlocal

除了上面幾種解決思路以外,JDK還提供了另一種用空間換時間的新思路:threadlocal。它的核心思想是:共享變量在每一個線程都有一個副本,每一個線程操做的都是本身的副本,對另外的線程沒有影響。特別注意,使用threadlocal時,使用完以後,要記得調用remove方法,否則可能會出現內存泄露問題。code

public class ThreadLocalService {

    private ThreadLocal<Integer> threadLocal = new ThreadLocal<>();


    public void add(int i) {
        Integer integer = threadLocal.get();
        threadLocal.set(integer == null ? 0 : integer + i);
    }
}

總結

本文介紹了8種多線程狀況下保證數據安全的技術手段,固然實際工做中可能會有其餘。技術沒有好壞之分,主要是看使用的場景,須要在不一樣的場景下使用不一樣的技術。對象

相關文章
相關標籤/搜索