java線程安全問題原因及解決辦法

1.為什么會出現線程安全問題

計算機系統資源分配的單位為進程,同一個進程中允許多個線程并發執行,并且多個線程會共享進程范圍內的資源:例如內存地址。當多個線程并發訪問同一個內存地址并且內存地址保存的值是可變的時候可能會發生線程安全問題,因此需要內存數據共享機制來保證線程安全問題。

對應到java服務來說,在虛擬中的共享內存地址是java的堆內存,比如以下程序中線程安全問題:

public class ThreadUnsafeDemo {private static final ExecutorService EXECUTOR_SERVICE;static {EXECUTOR_SERVICE = new ThreadPoolExecutor(100, 100, 1000 * 10,TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(100), new ThreadFactory() {private AtomicLong atomicLong = new AtomicLong(1);@Overridepublic Thread newThread(Runnable r) {return new Thread(r, "Thread-Safe-Thread-" + atomicLong.getAndIncrement());}});}public static void main(String[] args) throws Exception {Map<String, Integer> params = new HashMap<>();List<Future> futureList = new ArrayList<>(100);for (int i = 0; i < 100; i++) {futureList.add(EXECUTOR_SERVICE.submit(new CacheOpTask(params)));}for (Future future : futureList) {System.out.println("Future result:" + future.get());}System.out.println(params);}private static class CacheOpTask implements Callable<Integer> {private Map<String, Integer> params;CacheOpTask(Map<String, Integer> params) {this.params = params;}@Overridepublic Integer call() {for (int i = 0; i < 100; i++) {int count = params.getOrDefault("count", 0);params.put("count", ++count);}return params.get("count");}}
}

創建100個task,每個task對map中的元素累加100此,程序執行結果為:

{count=9846}

而預期的正確結果為:

{count=10000}

至于出現這種問題的原因,下面會具體分析。

判斷是否有線程安全性的一個原則是:

是否有多線程訪問可變的共享變量

2.多線程的優勢

發揮多處理器的強大能力,提高效率和程序吞吐量

3.并發帶來的風險

使用并發程序帶來的主要風險有以下三種:

3.1.安全性問題:

競態條件:由于不恰當的執行時序而出現不正確的結果

對于1中的線程安全的例子就是由于競態條件導致的最終結果與預期結果不一致。關鍵代碼塊如下:

int count = params.getOrDefault("count", 0);
params.put("count", ++count);

當多個線程同時取的count的值的時候,每個線程計算之后,在寫入到count,這時候會出現多個線程值被覆蓋的情況,最終導致結果不正確。
如下圖所示:929184-20180715150700914-1983734855.png

3.2解決此類問題的幾種方法

1.使用同步機制限制變量的訪問:鎖
比如:

synchronized (LOCK) {int count = params.getOrDefault("count", 0);params.put("count", ++count);
}

2.將變量設置為不可變

即將共享變量設置為final

3.不在線程之間共享此變量ThreadLocal

編程的原則:首先編寫正確的代碼,然后在實現性能的提升
無狀態的類一定是線程安全的

3.3 內置鎖

內置鎖:同步代碼塊( synchronized (this) {})

進入代碼塊前需要獲取鎖,會有性能問題。內置鎖是可重入鎖,之所以每個對象都有一個內置鎖,是為了避免顯示的創建鎖對象

常見的加鎖約定:將所有的可變狀態都封裝在對象內部,并使用內置鎖對所有訪問可變狀態的代碼進行同步。例如:Vector等

同步的另一個功能:內存可見性,類似于volatile

非volatile的64位變量double、long:
JVM允許對64位的操作分解為兩次32位的兩次操作,可變64位變量必須用volatile或者鎖來保護

加鎖的含義不僅在于互斥行為,還包括內存可見性,為了所有線程都可以看到共享變量的最新值,所有線程應該使用同一個鎖

原則:
==除非需要跟高的可見性,否則應該將所有的域都聲明為私有的,除非需要某個域是可變的,否則應該講所有的域生命為final的==

2.活躍性問題

線程活躍性問題主要是由于加鎖不正確導致的線程一直處于等待獲取鎖的狀態,比如以下程序:

public class DeadLock {private static final Object[] LOCK_ARRAY;static {LOCK_ARRAY = new Object[2];LOCK_ARRAY[0] = new Object();LOCK_ARRAY[1] = new Object();}public static void main(String[] args) throws Exception {TaskOne taskOne = new TaskOne();taskOne.start();TaskTwo taskTwo = new TaskTwo();taskTwo.start();System.out.println("finished");}private static class TaskOne extends Thread {@Overridepublic void run(){synchronized (LOCK_ARRAY[0]) {try {Thread.sleep(3000);} catch (Exception e) {}System.out.println("Get LOCK-0");synchronized (LOCK_ARRAY[1]) {System.out.println("Get LOCK-1");}}}}private static class TaskTwo extends Thread {@Overridepublic void run() {synchronized (LOCK_ARRAY[1]) {try {Thread.sleep(1000 * 3);} catch (Exception e) {}System.out.println("Get LOCK-1");synchronized (LOCK_ARRAY[0]) {System.out.println("Get LOCK-0");}}}}
}

在兩個線程持有一個鎖,并在在鎖沒有釋放之前,互相等待對方持有的鎖,這時候會造成兩個線程會一直等待,從而產生死鎖。在我們使用鎖的時候應該考慮持有鎖的時長,特別是在網絡I/O的時候。

在使用鎖的時候要盡量避免以上情況,從而避免產生死鎖

3.性能問題

在使用多線程執行程序的時候,在線程間的切換以及線程的調度也會消耗CPU的性能。

轉載于:https://www.cnblogs.com/vitasyuan/p/9313625.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/282768.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/282768.shtml
英文地址,請注明出處:http://en.pswp.cn/news/282768.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

html語言怎么添加圖片,我想問你一下,你是怎么在html中插入本地圖片?非常感謝...

滿意答案小蜜蜂手工2013.10.03采納率&#xff1a;43% 等級&#xff1a;12已幫助&#xff1a;7929人img{float:right}在下面的段落中&#xff0c;我們添加了一個樣式為 float:right 的圖像。結果是這個圖像會浮動到段落的右側。This is some text. This is some text. This i…

數組實現矩陣逐層向內層加1

package java1701;public class javaMain { public static void main(String[] args) { // 逐層加 // 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 // 1 2 2 2 2 1 1 2 2 2 1 1 2 2 1 1 2 1 // 1 2 3 3 2 1 1 2 3 2 1 1 2 2 1 1 1 1 // 1 2 3 …

EntityFrameworkCore上下文如何實現繼承?

【導讀】如果我們存在基礎設施服務和其他服務&#xff0c;我們會定義屬于基礎設施服務的上下文以及其他服務的上下文&#xff0c; 而且會獨立部署&#xff0c;此時其他服務需要使用基礎服務&#xff0c;我們都會暴露基礎服務接口給到其他服務調用&#xff0c;這也是常規操作若在…

Unity 游戲框架搭建 (九) 減少加班利器-QConsole

為毛要實現這個工具? 在我小時候,每當游戲在真機運行時,我們看到的日志是這樣的。 沒高亮啊,還有亂七八糟的堆棧信息,好干擾日志查看,好影響心情。 還有就是必須始終連著usb線啊&#xff0c;我想要想躺著測試。。。 以上種種原因,QConsole誕生了。 如何使用? 使用方式和QLog…

android藍牙多次后,android – 如何防止BluetoothGattCallback一次多次執行

我的服務有一個BluetoothGattCallback實例public class MyService extends Service {private BluetoothGattCallback callback;Overridepublic void onCreate() {super.onCreate();callback new BluetoothGattCallback() {Overridepublic synchronized void onConnectionState…

美觀又實用,10 款強大的開源 Javascript 圖表庫

2019獨角獸企業重金招聘Python工程師標準>>> 隨著發展&#xff0c;現代 Web 設計在改善體驗和功能的同時&#xff0c;對于美觀的追求也越來越高&#xff0c;可視化、交互式、動態等元素和效果似乎已成為標配。 以下是為開發者推薦的 10 款開源 Javascript 圖表庫&am…

EF CORE 7 RC1 發布

原文鏈接&#xff1a;https://devblogs.microsoft.com/dotnet/announcing-ef7-rc1/[1]原文作者&#xff1a;Jeremy Likness翻譯&#xff1a;沙漠盡頭的狼(谷歌翻譯加持)Entity Framework Core 7 (EF7) Release Candidate 1 已發布&#xff01;該團隊專注于解決缺陷、小幅改進以…

0 重新學習Ubuntu -- 這一段沒怎么學習

在完成了前面的幾個學習后&#xff0c;再沒有進行系統的學習。 雖然在真機上安裝系統&#xff0c;每天都打開&#xff0c;完成以下的工作&#xff1a; 升級軟件用來查看相關的網站在Ubuntu上&#xff0c;現在可以完成辦公、上網、娛樂。 但專業的學習&#xff0c;例如編程方面進…

自定義地圖怎么做成html,自定義html為谷歌地圖制作標記

好吧&#xff0c;似乎Custom Overlays會做我想要的。這是ping層&#xff1a;function PingLayer(bounds, map) {this.bounds bounds;this.setMap(map);}PingLayer.prototype new google.maps.OverlayView();PingLayer.prototype.onAdd function() {var div document.create…

HDU5248:序列變換(二分)

序列變換 Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 1348 Accepted Submission(s): 593Problem Description給定序列A{A1,A2,...,An}, 要求改變序列A中的某些元素&#xff0c;形成一個嚴格單調的序列B&am…

微服務太分散?使用Fundebug集中式bug監控

摘要&#xff1a; 微服務日志分散&#xff0c;可以使用Fundebug的異常監控將它們集中起來。 當一個項目復雜到一定程度&#xff0c;功能越來越多&#xff0c;隨之對應的模塊也越來越多。 如果都放在一個大的項目下面&#xff0c;共同開發&#xff0c;整合發布&#xff0c;那么會…

html404頁面怎么添加,網站要如何設置自定義404頁面?

之前我們講述過網站設置404頁面對于優化或是用戶體驗的重要意義&#xff0c;大家可移步到《網站為什么要設置404頁面》查看&#xff0c;今天我們講解的是網站要如何設置自己的404頁面。現在大多數空間商都有了404設置的功能&#xff0c;我們可將404頁面上傳至空間里面&#xff…

設計模式之——工廠方法模式

1、工廠方法模式&#xff08;Factory Method&#xff09;工廠方法模式分為三種&#xff1a;11、普通工廠模式&#xff0c;就是建立一個工廠類&#xff0c;對實現了同一接口的一些類進行實例的創建。首先看下關系圖&#xff1a;舉例如下&#xff1a;&#xff08;我們舉一個發送郵…

記一次性能故障排查

最近一次公司服務出了一些性能的問題&#xff0c;主要是內存不釋放。領到任務后就開始展開工作。項目是用.net core 6寫的&#xff0c;在框上應該不會有什么問題&#xff0c;這是大背景。另外服務是部署在k8s上的&#xff0c;于是就和性能測試人員&#xff0c;開發人員搭測試環…

html單選框 點擊取消選中,radio單選框再點擊取消選中

html:html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">單選框選項a選項b選項c選項dcheckradio.js://參數&#xff1a;obj為當前點擊的radio對象function onClickRadioStyle(obj){var…

開啟AngularJS 1.X的學習之路(1)

概念(1) AngularJS 應用 AngularJS 模塊&#xff08;Module&#xff09; 定義了 AngularJS 應用。AngularJS 控制器&#xff08;Controller&#xff09; 用于控制 AngularJS 應用。ng-app指令定義了應用, ng-controller 定義了控制器。eg: <div ng-app"myApp" ng-…

Hello boke!

Hello boke&#xff01;轉載于:https://www.cnblogs.com/yikuan-919/p/9319071.html

ASP.NET Core在.NET 7 RC1中的更新

原文鏈接&#xff1a;https://devblogs.microsoft.com/dotnet/asp-net-core-updates-in-dotnet-7-rc-1/[1]原文作者&#xff1a;Daniel Roth翻譯&#xff1a;沙漠盡頭的狼(谷歌翻譯加持).NET 7 Release Candidate 1 (RC1) 現已推出[2]&#xff0c;其中包括對 ASP.NET Core 的許…

html5 tab菜單切換頁面,11個常用的jQuery TAB切換菜單源碼及制作教程

11個常用的jQuery TAB切換菜單源碼及制作教程SponsorTAB切換式菜單可以方便為我們減少很多網頁布局空間&#xff0c;而且用jQuery的話可以加入一些動畫效果&#xff0c;比如漸變&#xff0c;向左右滑動等&#xff0c;提升一定的用戶體驗&#xff0c;所以TAB菜單目前來說是很流行…

7.16 10.19-10.22

10.19 iptables規則備份和恢復[roothyc-01-01 ~]# service iptables save 保存iptables規則該命令會將規則保存在/etc/sysconfig/iptables將iptables規則備份到一個文件中[roothyc-01-01 ~]# iptables-save>/tmp/ipt.txt將iptables規則備份到ipt.txt文件中從備份規則的文件恢…