併發狀況下如何保證數據安全,一直都是開發人員天天都要面對的問題,稍不注意就會出現數據異常,形成不可挽回的結果。筆者根據本身的實際開發經驗,總結了下面幾種保證數據安全的技術手段:安全
咱們都知道只有多個線程訪問公共資源的時候,纔可能出現數據安全問題,那麼若是咱們沒有公共資源,是否是就沒有這個問題呢?多線程
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); } }
若是有些公共資源只是一個開關,只要求可見性,不要求原子性,這樣能夠用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"); } }
使用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); } } }
除了使用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(); } } }
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); } }
除了上面幾種解決思路以外,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種多線程狀況下保證數據安全的技術手段,固然實際工做中可能會有其餘。技術沒有好壞之分,主要是看使用的場景,須要在不一樣的場景下使用不一樣的技術。對象