这8种保证线程安全的技术你都知道吗?
并發情況下如何保證數據安全,一直都是開發人員每天都要面對的問題,稍不注意就會出現數據異常,造成不可挽回的結果。筆者根據自己的實際開發經驗,總結了下面幾種保證數據安全的技術手段:
無狀態
不可變
安全的發布
volatile
synchronized
lock
cas
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關鍵字定義來解決問題。
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顯示鎖的方式。它包含:可重入鎖、讀寫鎖 等更多更強大的功能,有個小問題就是需要手動釋放鎖,不過在編碼時提供了更多的靈活性。
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方法,不然可能會出現內存泄露問題。
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種多線程情況下保證數據安全的技術手段,當然實際工作中可能會有其他。技術沒有好壞之分,主要是看使用的場景,需要在不同的場景下使用不同的技術。
有道無術,術可成;有術無道,止于術
歡迎大家關注Java之道公眾號
好文章,我在看??
總結
以上是生活随笔為你收集整理的这8种保证线程安全的技术你都知道吗?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Lua语言学习-垃圾回收
- 下一篇: 教你彻底学会Java序列化和反序列化