hashmap为什么线程不安全_什么时候线程不安全?怎样做到线程安全?怎么扩展线程安全的类?...
順便再給大家推薦一套SpringCloud微服務(wù)教程,方便學(xué)習(xí):
SpringCloud微服務(wù)電商項目教程 - 老炮說Java-程序員編程資料和編程經(jīng)驗分享平臺?www.laopaojava.com教程主要包含下面內(nèi)容:
當(dāng)多個線程去訪問某個類時,如果類會表現(xiàn)出我們預(yù)期出現(xiàn)的行為,那么可以稱這個類是線程安全的。
什么時候會出現(xiàn)線程不安全?
操作并非原子。多個線程執(zhí)行某段代碼,如果這段代碼產(chǎn)生的結(jié)果受不同線程之間的執(zhí)行時序影響,而產(chǎn)生非預(yù)期的結(jié)果,即發(fā)生了競態(tài)條件,就會出現(xiàn)線程不安全;
常見場景:
解決方式是:當(dāng)前線程在操作這段代碼時,其它線程不能對進(jìn)行操作
常見方案:
synchronized 的可重入性
當(dāng)線程要去獲取它自己已經(jīng)持有的鎖是會成功的,這樣的鎖是可重入的,synchronized 是可重入的class Paxi {
public synchronized void sayHello(){
System.out.println("hello");
}
}
class MyClass extends Paxi{
public synchronized void dosomething(){
System.out.println("do thing ..");
super.sayHello();
System.out.println("over");
}
}
它的輸出為
do thing ..
hello
over
復(fù)制代碼
- 修改不可見。讀線程無法感知到其它線程寫入的值
常見場景:
常見方案:
不同步的情況下如何做到線程安全?
- Ad-hoc 線程封閉。即靠自己寫程序來實現(xiàn),比如保證程序只在單線程上對 volatile 進(jìn)行 讀取-修改-寫入
- 棧封閉。所有的操作都反生執(zhí)行線程的棧中,比如在方法中的一個局部變量
- ThreadLocal 類。內(nèi)部維護(hù)了每個線程和變量的一個獨(dú)立副本
- 使用 final 去修飾字段,這樣這個字段的 “值” 是不可改變的
- 創(chuàng)建一個不可變的類,來包含多個可變的數(shù)據(jù)。
如何構(gòu)造線程安全的類?
public static <T> List<T> synchronizedList(List<T> list) {
return (list instanceof RandomAccess ?
new SynchronizedRandomAccessList<>(list) :
new SynchronizedList<>(list));
}
復(fù)制代碼
SynchronizedList 的實現(xiàn), 注意此處用到的 mutex 是內(nèi)置鎖
static class SynchronizedList<E>extends SynchronizedCollection<E>
implements List<E> {
private static final long serialVersionUID = -7754090372962971524L;
final List<E> list;
public E get(int index) {
synchronized (mutex) {return list.get(index);}
}
public E set(int index, E element) {
synchronized (mutex) {return list.set(index, element);}
}
public void add(int index, E element) {
synchronized (mutex) {list.add(index, element);}
}
public E remove(int index) {
synchronized (mutex) {return list.remove(index);}
}
}
復(fù)制代碼
mutex 的實現(xiàn)
static class SynchronizedCollection<E> implements Collection<E>, >Serializable {private static final long serialVersionUID = 3053995032091335093L;final Collection<E> c; // Backing Collectionfinal Object mutex; // Object on which to synchronizeSynchronizedCollection(Collection<E> c) {if (c==null)throw new NullPointerException();this.c = c;mutex = this; // mutex實際上就是對象本身}什么是監(jiān)視器模式
java 的監(jiān)視器模式,將對象所有可變狀態(tài)都封裝起來,并由對象自己的內(nèi)置鎖來保護(hù), 即是一種實例封閉。比如 HashTable 就是運(yùn)用的監(jiān)視器模式。它的 get 操作就是用的 synchronized,內(nèi)置鎖,來實現(xiàn)的線程安全
public synchronized V get(Object key) {Entry tab[] = table;int hash = hash(key);int index = (hash & 0x7FFFFFFF) % tab.length;for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {if ((e.hash == hash) && e.key.equals(key)) {return e.value;}}return null; }- 內(nèi)置鎖
每個對象都有內(nèi)置鎖。內(nèi)置鎖也稱為監(jiān)視器鎖。或者可以簡稱為監(jiān)視器
線程執(zhí)行一個對象的用 synchronized 修飾的方法時,會自動的獲取這個對象的內(nèi)置鎖,方法返回時自動釋放內(nèi)置鎖,執(zhí)行過程中就算拋出異常也會自動釋放。
以下兩種寫法等效:
- 私有鎖
它也可以用來保護(hù)對象,相對內(nèi)置鎖,優(yōu)勢在于私有鎖可以有多個,同時可以讓客戶端代碼顯示的獲取私有鎖
- 類鎖
在 staic 方法上修飾的,一個類的所有對象共用一把鎖
如果一個類中的各個組件都是線程安全的,該類是否要處理線程安全問題?
視情況而定。
線程安全性分析
- Point 類本身是無法更改的,所以它是線程安全的,DVT 返回的 Point 方法也是線程安全的
- DVT 的方法 getLocations 返回的對象是不可修改的,是線程安全的
- setLocation 實際操作的是 ConcurrentHashMap 它也是線程安全的
綜上,DVT 的安全交給了‘locations’,它本身是線程安全的,DVT 本身雖沒有任何顯示的同步,也是線程安全。這種情況下,就是 DVT 的線程安全實際是委托給了‘locations’, 整個 DVT 表現(xiàn)出了線程安全。
只要多個狀態(tài)變量之間彼此獨(dú)立,組合的類并不會在其包含的多個狀態(tài)變量上增加不變性。依賴的增加則無法保證線程安全
setLower 和 setUpper 都是‘先檢查后執(zhí)行’的操作,但是沒有足夠的加鎖機(jī)制保證操作的原子性。假設(shè)原始范圍是 (0,10), 一個線程調(diào)用 setLower(5), 一個設(shè)置 setUpper(4) 錯誤的執(zhí)行時序?qū)⒖赡軐?dǎo)致結(jié)果為(5,4)
如何對現(xiàn)有的線程安全類進(jìn)行擴(kuò)展?
假設(shè)需要擴(kuò)展的功能為 ‘沒有就添加’。這里的 putIfAbsent 并不能帶來線程安全,原因是 list 的內(nèi)置鎖并不是 ListHelper, 也就是 putIfAbsent 相對 list 的其它方法并不是原子的。Collections.synchronizedList 是鎖在 list 本身的,正確方式為
public boolean putIfAbsent(E x){synchronized(list){boolean absent = !list.contains(x);if(absent){list.add(x);}return absent;} }另外可以不管要操作的類是否是線程安全,對類統(tǒng)一添加一層額外的鎖。實現(xiàn)參考 Collections.synchronizedList 方法
作者:爬蜥https://juejin.im/post/5b7d68f66fb9a019d80a9002總結(jié)
以上是生活随笔為你收集整理的hashmap为什么线程不安全_什么时候线程不安全?怎样做到线程安全?怎么扩展线程安全的类?...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: DSP28335 eCAP 测频
- 下一篇: 加仓减仓口诀_做短线必备口诀