日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java多线程之线程的安全性(一)

發布時間:2024/2/28 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java多线程之线程的安全性(一) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

對象的狀態:對象的狀態是指存儲在狀態變量(實例或靜態域)中的數據。對象的狀態還可能包括其他依賴對象的域。例如,HashMap的狀態不僅儲存在對象本身,還儲存在Map.Entry對象中。

多線程安全的概念:當多個線程訪問某個類時,不管運行時環境采用何種調度方式或者這些線程將如何交替執行,并且在主調代碼中不需要任何額外的同步或協同,這個類都能表現出正確的行為,那么這個類就是線程安全的。

多線程安全的核心:編寫線程安全的代碼,核心在于要對狀態訪問操作進行管理,特別是對共享的(Shared)和可變的(Mutable)狀態的訪問。

?

1.無狀態對象線程安全

如例子中的因式分解Servlet:

public class StatelessFactorizer implement Servlet{public void service(ServletRequest req,ServletResponse resp){BigInteger i = extractFromRequest(req);BingInteger []factorys = factor(i);encodeingIntoResponse(req,factorys);} }

它既不包含任何域,也不包含其他類中域的引用,所以他是無狀態的。因此每個線程執行的結果不會影響到其他線程。

?

2.盡可能使用現有的線程安全對象管理狀態

下面是訪問一次count+1的例子:

public class UnsafeCountingFactorizer implement Servlet{private long count = 0;public long getCount(){return count;}public void service(ServletRequest req,ServletResponse resp){count++;BigInteger i = extractFromRequest(req);BingInteger []factorys = factor(i);encodeingIntoResponse(req,factorys);} }

count++的操作是 讀取count的值 count+1 寫入count 三步,如果線程1執行到第二步還沒寫入count的時候,線程2開始執行了第一步,那么線程2讀取的count是線程1還沒賦值的count,于是就出現的線程安全問題。
下面是解決方法:

public class SafeCountingFactorizer implement Servlet{private final AtomicLong count = new AtomicLong(0); public long getCount(){return count.get(0);}public void service(ServletRequest req,ServletResponse resp){count.incrementAndGet();BigInteger i = extractFromRequest(req);BingInteger []factorys = factor(i);encodeingIntoResponse(req,factorys);} }

通過用AtomicLong來代替long類型的計數器,能夠確保所有對計數器狀態的訪問操作都是原子的。由于Servlet的狀態只有一個,也就是計數器的狀態,所以這個Servlet是線程安全的。

3.防止競態條件的出現

競態條件:由于不恰當的執行時序而出現不正確的結果。常見的情況是延遲初始化。

public class LazyInitRace{private ExpensiveObject instance = null;public ExpensiveObject getInstance(){if(instance == null){instance = new ExpensiveObject();}return instance;} }

上面的競態條件很容易出現線程安全問題,如果線程1和2同時進入if語句,那么這個單例模式不能達到它想要的效果。
下面是解決方法:

public class LazyInitRace{private static final Object obj = new Object(); private ExpensiveObject instance = null;public ExpensiveObject getInstance(){if(instance == null){Synchronized(obj){if(instance == null){instance = new ExpensiveObject();}}}return instance;} }

這叫雙重鎖,他能保證new只會執行一次,保證了執行順序,所以競態條件就不存在了。而且我們要保證這個鎖是同一個鎖。

4.對于多個變量的不變性條件,涉及的所有變量需要同一個鎖保護。

下面的例子是緩存因式分解結果,如果傳入值相同,返回緩存值。

public class UnSafeCountingFactorizer implement Servlet{//最后一次傳入的值private final AtomicReferece<BigInteger> lastNumber = new AtomicReference<Integer>();//最后一次傳入的緩存private final AtomicReferece<BigInteger[]> lastFactors = new AtomicReference<Integer[]>();public void service(ServletRequest req,ServletResponse resp){BigInteger i = extractFromRequest(req);if(i.equals(lastNumber)){encodeingIntoResponse(req,factorys);}else{BingInteger []factorys = factor(i);lastFactors.set(factors);lastNumber.set(i);encodeingIntoResponse(req,factorys);}} }

上面的例子,兩個狀態都是能保證原子性的,但是不變性條件同時包含兩個狀態,也就是只修改其中一個變量,那么在兩次修改操作之間,其他線程將發現不變性條件被破壞了。
解決方法:

public class UnSafeCountingFactorizer implement Servlet{//最后一次傳入的值private final AtomicReferece<BigInteger> lastNumber = new AtomicReference<Integer>();//最后一次傳入的緩存private final AtomicReferece<BigInteger[]> lastFactors = new AtomicReference<Integer[]>();public void service(ServletRequest req,ServletResponse resp){BigInteger i = extractFromRequest(req);BigInteger []factors = null;Synchronized(this){if(i.equals(lastNumber)){factors = lastFactors.clone();}}if(factors == null){factors = factor(i);Synchronized(this){lastNumber = i;lastFactorys = factors.chone();}}encodeingIntoResponse(req,factorys);}} }

重新構造了這個類之后,實現了簡單性和并發性的平衡,把同步代碼全部方法放在一個Synchronized可能會影響效率,而將同步代碼塊分解太小會影響簡單行和可讀性。

5.占資源太多的操作不要持有鎖

例如,當一個程序執行時間太長或者占有cpu資源太大,很可能會受到其他因素的影響,導致長時間持有鎖,導致其他進程長時間發生阻塞。

總結

以上是生活随笔為你收集整理的java多线程之线程的安全性(一)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。