導航:首頁 > 編程語言 > java線程安全的變數

java線程安全的變數

發布時間:2024-04-01 16:14:19

java中如何保證線程安全性

並發(concurrency)一個並不陌生的詞,簡單來說,就是cpu在同一時刻執行多個任務。

而Java並發則由多線程實現的。

在jvm的世界裡,線程就像不相乾的平行空間,串列在虛擬機中。(當然這是比較籠統的說法,線程之間是可以交互的,他們也不一定是串列。)

多線程的存在就是壓榨cpu,提高程序性能,還能減少一定的設計復雜度(用現實的時間思維設計程序)。

這么說來似乎線程就是傳說中的銀彈了,可事實告訴我們真正的銀彈並不存在。

多線程會引出很多難以避免的問題, 如死鎖,臟數據,線程管理的額外開銷,等等。更大大增加了程序設計的復雜度。

但他的優點依舊不可替代。

死鎖和臟數據就是典型的線程安全問題。

簡單來說,線程安全就是:在多線程環境中,能永遠保證程序的正確性。

只有存在共享數據時才需要考慮線程安全問題。

java內存區域:

其中,方法區和堆就是主要的線程共享區域。那麼就是說共享對象只可能是類的屬性域或靜態域。

了解了線程安全問題的一些基本概念後, 我們就來說說如何解決線程安全問題。我們來從一個簡單的servlet示例來分析:

1. 了解業務場景的線程模型

這里的線程模型指的是: 在該業務場景下, 可能出現的線程調用實況。

眾所周知,Servlet是被設計為單實例,在請求進入tomcat後,由Connector建立連接,再講請求分發給內部線程池中的Processor,

此時Servlet就處於一個多線程環境。即如果存在幾個請求同時訪問某個servlet,就可能會有幾個線程同時訪問該servlet對象。如圖:

線程模型,如果簡單的話,就在腦海模擬一下就好了,復雜的話就可以用紙筆或其他工具畫出來。

2. 找出共享對象

這里的共享對象就很明顯就是ReqCounterServlet。

3. 分析共享對象的不變性條件

不變性條件,這個名詞是在契約式編程的概念中的。不變性條件保證類的狀態在任何功能被執行後都保持在一個可接受的狀態。

這里可以引申出,不可變對象是線程安全的。(因為不可變對象就沒有不變性條件)

不變性條件則主要由對可變狀態的修改與訪問構成。

這里的servlet很簡單, 不變性條件大致可以歸納為: 每次請求進入時count計數必須加一,且計數必須正確。

在復雜的業務中, 類的不變性條件往往很難考慮周全。設計的世界是險惡的,只能小心謹慎,用測量去證明,最大程度地減少錯誤出現的幾率。

4. 用特定的策略解決線程安全問題。

如何解決的確是該流程的重點。目前分三種方式解決:

第一種,修改線程模型。即不在線程之間共享該狀態變數。一般這個改動比較大,需要量力而行。

第二種,將對象變為不可變對象。有時候實現不了。

第三種,就比較通用了,在訪問狀態變數時使用同步。 synchronized和Lock都可以實現同步。簡單點說,就是在你修改或訪問可變狀態時加鎖,獨占對象,讓其他線程進不來。

這也算是一種線程隔離的辦法。(這種方式也有不少缺點,比如說死鎖,性能問題等等)

其實有一種更好的辦法,就是設計線程安全類。《代碼大全》就有提過,問題解決得越早,花費的代價就越小。

是的,在設計時,就考慮線程安全問題會容易的多。

首先考慮該類是否會存在於多線程環境。如果不是,則不考慮線程安全。

然後考慮該類是否能設計為不可變對象,或者事實不可變對象。如果是,則不考慮線程安全

最後,根據流程來設計線程安全類。

設計線程安全類流程:

1、找出構成對象狀態的所有變數。

2、找出約束狀態變數的不變性條件。

3、建立對象狀態的並發訪問管理策略。

有兩種常用的並發訪問管理策略:

1、java監視器模式。 一直使用某一對象的鎖來保護某狀態。

2、線程安全委託。將類的線程安全性委託給某個或多個線程安全的狀態變數。(注意多個時,這些變數必須是彼此獨立,且不存在相關聯的不變性條件。)

② java 線程安全是什麼說說概念就行了

線程安全是指要控制多個線程對某個資源的有序訪問或修改,而在這些線程之間沒有產生沖突。
在Java里,線程安全一般體現在兩個方面:

1、多個thread對同一個java實例的訪問(read和modify)不會相互干擾,它主要體現在關鍵字synchronized。如ArrayList和Vector,HashMap和Hashtable(後者每個方法前都有synchronized關鍵字)。如果你在interator一個List對象時,其它線程remove一個element,問題就出現了。

2、每個線程都有自己的欄位,而不會在多個線程之間共享。它主要體現在java.lang.ThreadLocal類,而沒有Java關鍵字支持,如像static、transient那樣。

③ 合理使用線程池以及線程變數

背景

隨著計算技術的不斷發展,3納米製程晶元已進入試產階段,摩爾定律在現有工藝下逐漸面臨巨大的物理瓶頸,通過多核處理器技術來提升伺服器的性能成為提升算力的主要方向。

在伺服器領域,基於java構建的後端伺服器占據著領先地位,因此,掌握java並發編程技術,充分利用CPU的並發處理能力是一個開發人員必修的基本功,本文結合線程池源碼和實踐,簡要介紹了線程池和線程變數的使用。

線程池概述

線程池是一種「池化」的線程使用模式,通過創建一定數量的線程,讓這些線程處於就緒狀態來提高系統響應速度,在線程使用完成後歸還到線程池來達到重復利用的目標,從而降低系統資源的消耗。

總體來說,線程池有如下的優勢:

線程池的使用

在java中,線程池的實現類是ThreadPoolExecutor,構造函數如下:

可以通過 new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory,handler)來創建一個線程池。

在構造函數中,corePoolSize為線程池核心線程數。默認情況下,核心線程會一直存活,但是當將allowCoreThreadTimeout設置為true時,核心線程超時也會回收。

在構造函數中,maximumPoolSize為線程池所能容納的最首高模大線程數。

在構造函數中,keepAliveTime表示線程閑置超時時長。如果線程閑置時間超過該時長,非核心線程就會被回收。如果將allowCoreThreadTimeout設置為true時,核心線程也會超時回收。

在構造函數中,timeUnit表示線程閑置超時時長的時間單位。常用的有:TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECONDS(秒)、TimeUnit.MINUTES(分)。

在構造函數中,blockingQueue表示任務隊列,線程池任務隊列的常用實現類有:

在構造函數中,threadFactory表示線程工廠。用於指定為線程池創建新線程的方式,threadFactory可以設置線程名稱、線程組、優先順序等參數。如通過Google工具包可以設置線程池裡的線程名:

在構造函數中,rejectedExecutionHandler表示拒絕策略。當達到最大線程數且隊列任務已滿時需要執行的拒絕策略,常見的拒絕策略如下:


ThreadPoolExecutor線程池有如下幾種狀態:



線程池提交一個任務時任務調度的主要步驟如下:

核心代碼如下:


Tomcat 的整體架構包含連接器和容器兩大部分,其中連接器負責與外部通信,容器負責內部邏輯處理。在連接器中:

Tomcat為了實現請者緩求的快念鉛速響應,使用線程池來提高請求的處理能力。下面我們以HTTP非阻塞I/O為例對Tomcat線程池進行簡要的分析。

在Tomcat中,通過AbstractEndpoint類提供底層的網路I/O的處理,若用戶沒有配置自定義公共線程池,則AbstractEndpoint通過createExecutor方法來創建Tomcat默認線程池。

核心部分代碼如下:

其中,TaskQueue、ThreadPoolExecutor分別為Tomcat自定義任務隊列、線程池實現。

Tomcat自定義線程池繼承於java.util.concurrent.ThreadPoolExecutor,並新增了一些成員變數來更高效地統計已經提交但尚未完成的任務數量(submittedCount),包括已經在隊列中的任務和已經交給工作線程但還未開始執行的任務。

Tomcat在自定義線程池ThreadPoolExecutor中重寫了execute()方法,並實現對提交執行的任務進行submittedCount加一。Tomcat在自定義ThreadPoolExecutor中,當線程池拋出RejectedExecutionException異常後,會調用force()方法再次向TaskQueue中進行添加任務的嘗試。如果添加失敗,則submittedCount減一後,再拋出RejectedExecutionException。

在Tomcat中重新定義了一個阻塞隊列TaskQueue,它繼承於LinkedBlockingQueue。在Tomcat中,核心線程數默認值為10,最大線程數默認為200, 為了避免線程到達核心線程數後後續任務放入隊列等待,Tomcat通過自定義任務隊列TaskQueue重寫offer方法實現了核心線程池數達到配置數後線程的創建。

具體地,從線程池任務調度機制實現可知,當offer方法返回false時,線程池將嘗試創建新新線程,從而實現任務的快速響應。TaskQueue核心實現代碼如下:

Tomcat中通過自定義任務線程TaskThread實現對每個線程創建時間的記錄;使用靜態內部類WrappingRunnable對Runnable進行包裝,用於對StopPooledThreadException異常類型的處理。

Executors常用方法有以下幾個:

Executors類看起來功能比較強大、用起來還比較方便,但存在如下弊端

使用線程時,可以直接調用 ThreadPoolExecutor 的構造函數來創建線程池,並根據業務實際場景來設置corePoolSize、blockingQueue、RejectedExecuteHandler等參數。

使用局部線程池時,若任務執行完後沒有執行shutdown()方法或有其他不當引用,極易造成系統資源耗盡。

在工程實踐中,通常使用下述公式來計算核心線程數:

nThreads=(w+c)/c*n*u=(w/c+1)*n*u

其中,w為等待時間,c為計算時間,n為CPU核心數(通常可通過 Runtime.getRuntime().availableProcessors()方法獲取),u為CPU目標利用率(取值區間為[0, 1]);在最大化CPU利用率的情況下,當處理的任務為計算密集型任務時,即等待時間w為0,此時核心線程數等於CPU核心數。

上述計算公式是理想情況下的建議核心線程數,而不同系統/應用在運行不同的任務時可能會有一定的差異,因此最佳線程數參數還需要根據任務的實際運行情況和壓測表現進行微調。

為了更好地發現、分析和解決問題,建議在使用多線程時增加對異常的處理,異常處理通常有下述方案:

為了實現優雅停機的目標,我們應當先調用shutdown方法,調用這個方法也就意味著,這個線程池不會再接收任何新的任務,但是已經提交的任務還會繼續執行。之後我們還應當調用awaitTermination方法,這個方法可以設定線程池在關閉之前的最大超時時間,如果在超時時間結束之前線程池能夠正常關閉則會返回true,否則,超時會返回false。通常我們需要根據業務場景預估一個合理的超時時間,然後調用該方法。

如果awaitTermination方法返回false,但又希望盡可能在線程池關閉之後再做其他資源回收工作,可以考慮再調用一下shutdownNow方法,此時隊列中所有尚未被處理的任務都會被丟棄,同時會設置線程池中每個線程的中斷標志位。shutdownNow並不保證一定可以讓正在運行的線程停止工作,除非提交給線程的任務能夠正確響應中斷。

ThreadLocal線程變數概述

ThreadLocal類提供了線程本地變數(thread-local variables),這些變數不同於普通的變數,訪問線程本地變數的每個線程(通過其get或set方法)都有其自己的獨立初始化的變數副本,因此ThreadLocal沒有多線程競爭的問題,不需要單獨進行加鎖。

ThreadLocal的原理與實踐

對於ThreadLocal而言,常用的方法有get/set/initialValue 3個方法。

眾所周知,在java中SimpleDateFormat有線程安全問題,為了安全地使用SimpleDateFormat,除了1)創建SimpleDateFormat局部變數;和2)加同步鎖 兩種方案外,我們還可以使用3)ThreadLocal的方案:

Thread 內部維護了一個 ThreadLocal.ThreadLocalMap 實例(threadLocals),ThreadLocal 的操作都是圍繞著 threadLocals 來操作的。

從JDK源碼可見,ThreadLocalMap中的Entry是弱引用類型的,這就意味著如果這個ThreadLocal只被這個Entry引用,而沒有被其他對象強引用時,就會在下一次GC的時候回收掉。

EagleEye(鷹眼)作為全鏈路監控系統在集團內部被廣泛使用,traceId、rpcId、壓測標等信息存儲在EagleEye的ThreadLocal變數中,並在HSF/Dubbo服務調用間進行傳遞。EagleEye通過Filter將數據初始化到ThreadLocal中,部分相關代碼如下:

在EagleEyeFilter中,通過EagleEyeRequestTracer.startTrace方法進行初始化,在前置入參轉換後,通過startTrace重載方法將鷹眼上下文參數存入ThreadLocal中,相關代碼如下:

EagleEyeFilter在finally代碼塊中,通過EagleEyeRequestTracer.endTrace方法結束調用鏈,通過clear方法將ThreadLocal中的數據進行清理,相關代碼實現如下:

在某權益領取原有鏈路中,通過app打開一級頁面後才能發起權益領取請求,請求經過淘系無線網關(Mtop)後到達服務端,服務端通過mtop sdk獲取當前會話信息。

在XX項目中,對權益領取鏈路進行了升級改造,在一級頁面請求時,通過服務端同時發起權益領取請求。具體地,服務端在處理一級頁面請求時,同時通過調用hsf/bbo介面來進行權益領取,因此在發起rpc調用時需要攜帶用戶當前會話信息,在服務提供端將會話信息進行提取並注入到mtop上下文,從而才能通過mtop sdk獲取到會話id等信息。某開發同學在實現時,因ThreadLocal使用不當造成下述問題:

【問題1:權益領取失敗分析】

在權益領取服務中,該應用構建了一套高效和線程安全的依賴注入框架,基於該框架的業務邏輯模塊通常抽象為xxxMole形式,Mole間為網狀依賴關系,框架會按依賴關系自動調用init方法(其中,被依賴的mole 的init方法先執行)。

在應用中,權益領取介面的主入口為CommonXXApplyMole類,CommonXXApplyMole依賴XXSessionMole。當請求來臨時,會按依賴關系依次調用init方法,因此XXSessionMole的init方法會優先執行;而開發同學在CommonXXApplyMole類中的init方法中通過調用recoverMtopContext()方法來期望恢復mtop上下文,因recoverMtopContext()方法的調用時機過晚,從而導致XXSessionMole模塊獲取不到正確的會話id等信息而導致權益領取失敗。

【問題2:臟數據分析】

權益領取服務在處理請求時,若當前線程曾經處理過權益領取請求,因ThreadLocal變數值未被清理,此時XXSessionMole通過mtop SDK獲取會話信息時得到的是前一次請求的會話信息,從而造成臟數據。

【解決方案】

在依賴注入框架入口處AbstractGate#visit(或在XXSessionMole中)通過recoverMtopContext方法注入mtop上下文信息,並在入口方法的finally代碼塊清理當前請求的threadlocal變數值。

若使用強引用類型,則threadlocal的引用鏈為:Thread -> ThreadLocal.ThreadLocalMap -> Entry[] -> Entry -> key(threadLocal對象)和value;在這種場景下,只要這個線程還在運行(如線程池場景),若不調用remove方法,則該對象及關聯的所有強引用對象都不會被垃圾回收器回收。

若使用static關鍵字進行修飾,則一個線程僅對應一個線程變數;否則,threadlocal語義變為perThread-perInstance,容易引發內存泄漏,如下述示例:

在上述main方法第22行debug,可見線程的threadLocals變數中有3個threadlocal實例。在工程實踐中,使用threadlocal時通常期望一個線程只有一個threadlocal實例,因此,若不使用static修飾,期望的語義發生了變化,同時易引起內存泄漏。

如果不執行清理操作,則可能會出現:

建議使用try...finally 進行清理。

我們在使用ThreadLocal時,通常期望的語義是perThread,若不使用static進行修飾,則語義變為perThread-perInstance;在線程池場景下,若不用static進行修飾,創建的線程相關實例可能會達到 M * N個(其中M為線程數,N為對應類的實例數),易造成內存泄漏(https://errorprone.info/bugpattern/ThreadLocalUsage)。

在應用中,謹慎使用ThreadLocal.withInitial(Supplier<? extends S> supplier)這個工廠方法創建ThreadLocal對象,一旦不同線程的ThreadLocal使用了同一個Supplier對象,那麼隔離也就無從談起了,如:

總結

在java工程實踐中,線程池和線程變數被廣泛使用,因線程池和線程變數的不當使用經常造成安全生產事故,因此,正確使用線程池和線程變數是每一位開發人員必須修煉的基本功。本文從線程池和線程變數的使用出發,簡要介紹了線程池和線程變數的原理和使用實踐,各開發人員可結合最佳實踐和實際應用場景,正確地使用線程和線程變數,構建出穩定、高效的java應用服務。

④ java如何實現靜態變數多線程安全問題

public class A { public void method01(){ //dosomething } public void method02(){ //dosomething } //public void method03(){ public static void method03(){ // 靜態方法里才可以有今天變數 static Map map = new TreeMap(); static String x=「」; // 如果你的 method4,5,6是非同步的, 5,6得到的值就是不確定的, // 有可能是4之前的值,也有可能是4的賦值 method4(){ 這裡面會給map和x賦值 } method5(){ 這裡面會用到map和x } method6(){ 這裡面會用到map和x } }}

⑤ java里線程安全是什麼意思有什麼作用

比如說,兩個線程操作同一個ArrayList變數,那麼一個線程這一時刻讀的數據可能在下一刻要改變。

一般在類似於下面的情景下考慮線程安全的問題:

ArrayList procts=new ArrayList ();
procts用來存放生產出來的產品。
現在假設:有3個消費者線程,2個生產者線程。
每個生產者線程生產出一個產品,執行
procts.add(new Proct());
每個消費者線程消費一個產品執行
if(procts.size()>=1){ procts.remove(0);}

如果procts里現在只有一個產品可以消費,但是有2個消費者線程請求消費,那麼就有可能出現一個產品被同時消費的問題,而這是和實際不符的。

但是不同的線程訪問Vector的時候不會發生這種錯誤,因為java會有相應的機制是同一時刻只有一個線程對這個變數操作。

這就是所謂的:
Vector:是線程安全的
ArrayList:不是線程安全的

⑥ JAVA涓濡備綍淇濊瘉綰跨▼瀹夊叏浠ュ強涓婚敭鑷澧炴湁搴

JAVA涓濡備綍淇濊瘉綰跨▼瀹夊叏浠ュ強涓婚敭鑷澧炴湁搴

涓銆佸父瑙佸満鏅

澶氫釜綰跨▼閽堝逛竴涓猧榪涜屼富閿鑷澧炪傚氱嚎紼嬩笅濡傛灉涓嶅仛瀹夊叏絳栫暐錛屽皢浼氬艱嚧鍚勪釜鐜版垚鑾峰彇鐨剗鍊奸噸澶嶏紝瀵艱嚧鑴忔暟鎹

甯歌佺瓥鐣

1銆佸炲姞syschroize榪涜岀嚎紼嬪悓姝

2銆佷嬌鐢╨ock銆乽nlock澶勭悊

3銆佷嬌鐢╮eetrantent 閿佽繘琛岄攣瀹

緙虹偣錛氬規槗閫犳垚鎬ц兘浣庝笅錛屾垨鑰呯紪鍐欎唬鐮佸規槗閫犳垚姝婚攣

浜屻佹柊鏂規

jdk鏂版彁渚涚殑鍔熻兘錛宎tomicInteger錛堣繕鏈夊叾浠栦竴atomic寮澶寸殑鍘熷瓙鎬ф搷浣滅被錛

AtomicInteger錛屼竴涓鎻愪緵鍘熷瓙鎿嶄綔鐨処nteger鐨勭被銆傚湪Java璇璦涓錛++i鍜宨++鎿嶄綔騫朵笉鏄綰跨▼瀹夊叏鐨勶紝鍦ㄤ嬌鐢ㄧ殑鏃跺欙紝涓嶅彲閬垮厤鐨勪細鐢ㄥ埌synchronized鍏抽敭瀛椼傝孉tomicInteger鍒欓氳繃涓縐嶇嚎紼嬪畨鍏ㄧ殑鍔犲噺鎿嶄綔鎺ュ彛銆

鍘熺悊錛氶氳繃java鐨凜AS compare and swap錛岀畝縐癱as鍘熻榪涜屾搷浣滄彁鍗囨ц兘錛岃繖涓涔熷彿縐頒箰瑙傞攣錛屼笉闃誨

瑙傞攣瀹為檯涓婂苟涓嶅姞閿侊紝褰撹$畻閬囧埌鍐茬獊鎴栬呰村墠鍚庝笉涓鑷存椂浼氶噸璇 鐩村埌鎴愬姛

CAS鏈3涓鎿嶄綔鏁 鍐呭瓨鍊糣 瑕佽窡鍐呭瓨鍊煎仛姣旇緝鐨勫糀 鍜 鏂板 B

[html]view plain

⑦ 請簡要說明java中線程安全是怎麼回事

如果你的代碼所在的進程中有多個線程在同時運行,而這些線程可能會同時運行這段代碼。如果每次運行結果和單線程運行的結果是一樣的,而且其他的變數的值也和預期的是一樣的,就是線程安全的。
或者說:一個類或者程序所提供的介面對於線程來說是原子操作或者多個線程之間的切換不會導致該介面的執行結果存在二義性,也就是說我們不用考慮同步的問題。
線程安全問題都是由全局變數及靜態變數引起的。
若每個線程中對全局變數、靜態變數只有讀操作,而無寫操作,一般來說,這個全局變數是線程安全的;若有多個線程同時執行寫操作,一般都需要考慮線程同步,否則就可能影響線程安全。

比如一個 ArrayList 類,在添加一個元素的時候,它可能會有兩步來完成:1. 在 Items[Size] 的位置存放此元素;2. 增大 Size 的值。
在單線程運行的情況下,如果 Size = 0,添加一個元素後,此元素在位置 0,而且 Size=1;
而如果是在多線程情況下,比如有兩個線程,線程 A 先將元素存放在位置 0。但是此時 CPU 調度線程A暫停,線程 B 得到運行的機會。線程B也向此 ArrayList 添加元素,因為此時 Size 仍然等於 0 (注意哦,我們假設的是添加一個元素是要兩個步驟哦,而線程A僅僅完成了步驟1),所以線程B也將元素存放在位置0。然後線程A和線程B都繼續運行,都增加 Size 的值。
那好,現在我們來看看 ArrayList 的情況,元素實際上只有一個,存放在位置 0,而 Size 卻等於 2。這就是「線程不安全」了。
編輯本段線程安全性類要成為線程安全的,首先必須在單線程環境中有正確的行為。如果一個類實現正確(這是說它符合規格說明的另一種方式),那麼沒有一種對這個類的對象的操作序列(讀或者寫公共欄位以及調用公共方法)可以讓對象處於無效狀態,觀察到對象處於無效狀態、或者違反類的任何不可變數、前置條件或者後置條件的情況。
此外,一個類要成為線程安全的,在被多個線程訪問時,不管運行時環境執行這些線程有什麼樣的時序安排或者交錯,它必須仍然有如上所述的正確行為,並且在調用的代碼中沒有任何額外的同步。其效果就是,在所有線程看來,對於線程安全對象的操作是以固定的、全局一致的順序發生的。
正確性與線程安全性之間的關系非常類似於在描述 ACID(原子性、一致性、獨立性和持久性)事務時使用的一致性與獨立性之間的關系:從特定線程的角度看,由不同線程所執行的對象操作是先後(雖然順序不定)而不是並行執行的。

線程安全性不是一個非真即假的命題。 Vector 的方法都是同步的,並且 Vector 明確地設計為在多線程環境中工作。但是它的線程安全性是有限制的,即在某些方法之間有狀態依賴(類似地,如果在迭代過程中 Vector 被其他線程修改,那麼由 Vector.iterator() 返回的 iterator會拋出)。
對於 Java 類中常見的線程安全性級別,沒有一種分類系統可被廣泛接受,不過重要的是在編寫類時盡量記錄下它們的線程安全行為。
Bloch 給出了描述五類線程安全性的分類方法:不可變、線程安全、有條件線程安全、線程兼容和線程對立。只要明確地記錄下線程安全特性,那麼您是否使用這種系統都沒關系。這種系統有其局限性 -- 各類之間的界線不是百分之百地明確,而且有些情況它沒照顧到 -- 但是這套系統是一個很好的起點。這種分類系統的核心是調用者是否可以或者必須用外部同步包圍操作(或者一系列操作)。下面幾節分別描述了線程安全性的這五種類別。不可變不可變的對象一定是線程安全的,並且永遠也不需要額外的同步[1]。因為一個不可變的對象只要構建正確,其外部可見狀態永遠也不會改變,永遠也不會看到它處於不一致的狀態。Java 類庫中大多數基本數值類如 Integer 、 String 和 BigInteger 都是不可變的。線程安全線程安全的對象具有在上面「線程安全」一節中描述的屬性 -- 由類的規格說明所規定的約束在對象被多個線程訪問時仍然有效,不管運行時環境如何排列,線程都不需要任何額外的同步。這種線程安全性保證是很嚴格的 -- 許多類,如 Hashtable 或者 Vector 都不能滿足這種嚴格的定義。有條件的線程安全有條件的線程安全類對於單獨的操作可以是線程安全的,但是某些操作序列可能需要外部同步。條件線程安全的最常見的例子是遍歷由 Hashtable 或者 Vector 或者返回的迭代器 -- 由這些類返回的 fail-fast 迭代器假定在迭代器進行遍歷的時候底層集合不會有變化。為了保證其他線程不會在遍歷的時候改變集合,進行迭代的線程應該確保它是獨占性地訪問集合以實現遍歷的完整性。通常,獨占性的訪問是由對鎖的同步保證的 -- 並且類的文檔應該說明是哪個鎖(通常是對象的內部監視器(intrinsic monitor))。
如果對一個有條件線程安全類進行記錄,那麼您應該不僅要記錄它是有條件線程安全的,而且還要記錄必須防止哪些操作序列的並發訪問。用戶可以合理地假設其他操作序列不需要任何額外的同步。線程兼容線程兼容類不是線程安全的,但是可以通過正確使用同步而在並發環境中安全地使用。這可能意味著用一個 synchronized 塊包圍每一個方法調用,或者創建一個包裝器對象,其中每一個方法都是同步的(就像 Collections.synchronizedList() 一樣)。也可能意味著用 synchronized 塊包圍某些操作序列。為了最大程度地利用線程兼容類,如果所有調用都使用同一個塊,那麼就不應該要求調用者對該塊同步。這樣做會使線程兼容的對象作為變數實例包含在其他線程安全的對象中,從而可以利用其所有者對象的同步。
許多常見的類是線程兼容的,如集合類 ArrayList 和 HashMap 、 java.text.SimpleDateFormat 、或者 JDBC 類 Connection 和 ResultSet 。線程對立線程對立類是那些不管是否調用了外部同步都不能在並發使用時安全地呈現的類。線程對立很少見,當類修改靜態數據,而靜態數據會影響在其他線程中執行的其他類的行為,這時通常會出現線程對立。線程對立類的一個例子是調用 System.setOut() 的類。

閱讀全文

與java線程安全的變數相關的資料

熱點內容
掃描版pdf轉文字版 瀏覽:532
單片機專用寄存器 瀏覽:495
學習python的手冊 瀏覽:676
vue編譯成js文件 瀏覽:90
給單片機供電的電池 瀏覽:341
什麼app是分享教育的 瀏覽:898
可視化編程java 瀏覽:83
人工智慧溫控器演算法 瀏覽:376
大號文件夾多少錢一個 瀏覽:572
pdf閱讀器打開文件 瀏覽:98
winrar解壓日文文件 瀏覽:38
什麼app可以看廣東珠江電視台 瀏覽:75
linux移動文件位置 瀏覽:144
循環碼與卷積碼編譯原理 瀏覽:807
進化演算法和啟發式演算法的區別 瀏覽:602
android組件是什麼 瀏覽:973
安卓手機微信怎麼同步信息 瀏覽:182
小人pdf 瀏覽:806
我的世界伺服器怎麼造好看的建築 瀏覽:307
兄弟連培訓php多少錢 瀏覽:250