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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

【从入门到放弃-Java】并发编程-线程安全

發布時間:2024/8/23 java 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【从入门到放弃-Java】并发编程-线程安全 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

概述

并發編程,即多條線程在同一時間段內“同時”運行。

在多處理器系統已經普及的今天,多線程能發揮出其優勢,如:一個8核cpu的服務器,如果只使用單線程的話,將有7個處理器被閑置,只能發揮出服務器八分之一的能力(忽略其它資源占用情況)。
同時,使用多線程,可以簡化我們對復雜任務的處理邏輯,降低業務模型的復雜程度。

因此并發編程對于提高服務器的資源利用率、提高系統吞吐量、降低編碼難度等方面起著至關重要的作用。

以上是并發編程的優點,但是它同樣引入了一個很重要的問題:線程安全。

什么是線程安全問題

線程在并發執行時,因為cpu的調度等原因,線程會交替執行。如下圖例子所示

public class SelfIncremental {private static int count;public static void main(String[] args) {Thread thread1 = new Thread(() -> {for (int i = 0; i< 10000; i++) {count++;System.out.println(count);}});Thread thread2 = new Thread(() -> {for (int i = 0; i< 10000; i++) {count++;System.out.println(count);}});thread1.start();thread2.start();} }

執行完畢后count的值并不是每次都能等于20000,會出現小于20000的情況,原因是thread1和thread2可能會交替執行。

如圖所示:

  • t1時刻: thread1 讀取到count=100
  • t2時刻: thread2 讀取到count=100
  • t3時刻: thread1 對count+1
  • t4時刻: thread2 對count+1
  • t5時刻: thread1 將101寫入count
  • t5時刻: thread2 將101寫入count

因為count++ 不是一個原子操作,實際上會執行三步:

  • 1、獲取count的值
  • 2、將count加1
  • 3、將計算結果寫入count

因此在并發執行時,兩個線程同時讀,可能會讀取到相同的值,對相同的值加一,導致結果不符合預期,這種情況就是線程不安全。

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

引發原因

引發線程安全性問題的原因主要是共享內存可以被多個線程讀寫,因為讀取和修改時機存在不確定性,導致有線程讀到了過期數據,并在臟數據的基礎上處理后寫回共享內存,產生了錯誤的結果。

竟態條件

在并發編程中,因為不恰當的執行時序而出現不正確的結果的情況被稱為竟態條件。

常見的靜態條件類型:

  • 先檢查后執行:首先觀察到某個條件為真。根據這個觀察結果采用相應的動作,但實際上在你觀察到這個結果和采用相應動作之間,觀察的結果可能發生改變變得無效,導致后續的所有操作都變得不可預期。(比如延遲初始化)
  • 讀取-修改-寫入:基于對象之前的狀態來定義對象狀態的轉換。但在讀取到結果和修改之間,對象可能已被更改。這樣就會基于錯誤的數據修改得出錯誤的結果并被寫入。(比如遞增操作)

發布與逸出

發布:使對象能夠在當前作用域之外的代碼中使用。如將該對象的引用保存到其它代碼可以訪問的地方、在一個非私有的方法中返回該引用,將引用傳遞到其它類的方法中。如:

public static Student student;public void init() { student = new Student; }

這里 student對象就被發布了。

逸出:當不該被發布的對象被發布了,就稱為逸出。如

private String name = "xxx";public String getString() {return name; }

這里name原為private類型但是卻被getString方法發布了,就可以被視為逸出。

如何避免

線程封閉

線程封閉的對象只能由一個線程擁有,對象被封閉在該線程中,并且只有這個對象能修改。

線程封閉即不共享數據,僅在單線程內訪問數據,這是實現線程安全最簡單的方式之一。
實現線程封閉可以通過:

  • Ad-hoc線程封閉:即維護線程封閉性的職責完全由成熟實現承擔。
  • 棧封閉:通過局部變量才能訪問對象,該局部變量被保存在執行線程的棧中,其他線程無法訪問。
  • ThreadLocal類:將共享的全局變量轉換為ThreadLocal對象,當線程終止后,這些值會被垃圾回收。

只讀共享

在沒有額外同步的情況下,共享的對象可以由多個線程并發訪問,但是任何線程都不能修改。共享的對象包括不可變對象和事實不可變對象。

不可變對象:如果某個對象在被創建后就不能修改,那么這個對象就是不可變對象。不可變對象一定是線程安全的。

線程安全共享

線程安全的對象在其內部實現同步,因此多線程可以通過對象的公有接口來進行訪問而不需要自己做同步。

保護對象

被保護的對象只能通過持有特定的鎖來訪問。即通過加鎖機制,確保對象的可見性及原子性。

  • 內置鎖:即通過synchronized關鍵字同步代碼塊。線程在進入同步代碼塊之前會自動獲得鎖,并在退出同步代碼塊時自動釋放鎖。內置鎖是一種互斥鎖。
  • 重入鎖:當線程視圖獲取一個已經持有的鎖時,就會給鎖的計數器加一,釋放鎖時計數器會減一。當計數器為0時,釋放鎖
  • volatile:訪問volatile變量時,不會加鎖,也不會阻塞線程執行。他只確保變量的可見性,是一種比synchronized更輕量級的同步機制。

總結

本文主要是記錄了學習《Java并發編程實站》前幾章中,并發編程相關的一些概念。簡單介紹了線程安全、鎖機制等,接下來 我們會深入JUC源碼,來深刻學習并發編程相關知識。

備注:本文主要源自對《Java并發編程實戰》的學習筆記。


原文鏈接
本文為云棲社區原創內容,未經允許不得轉載。

創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

總結

以上是生活随笔為你收集整理的【从入门到放弃-Java】并发编程-线程安全的全部內容,希望文章能夠幫你解決所遇到的問題。

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