當前位置:首頁 » 密碼管理 » java多線程互斥訪問

java多線程互斥訪問

發布時間: 2022-11-19 01:59:46

java多線程開發的同步機制有哪些

Java同步
標簽: 分類:

一、關鍵字:

thread(線程)、thread-safe(線程安全)、intercurrent(並發的)

synchronized(同步的)、asynchronized(非同步的)、

volatile(易變的)、atomic(原子的)、share(共享)

二、總結背景:

一次讀寫共享文件編寫,嚯,好傢伙,竟然揪出這些零碎而又是一路的知識點。於是乎,Google和翻閱了《Java參考大全》、《Effective Java Second Edition》,特此總結一下供日後工作學習參考。

三、概念:

1、 什麼時候必須同步?什麼叫同步?如何同步?

要跨線程維護正確的可見性,只要在幾個線程之間共享非 final 變數,就必須使用 synchronized(或 volatile)以確保一個線程可以看見另一個線程做的更改。

為了在線程之間進行可靠的通信,也為了互斥訪問,同步是必須的。這歸因於java語言規范的內存模型,它規定了:一個線程所做的變化何時以及如何變成對其它線程可見。

因為多線程將非同步行為引進程序,所以在需要同步時,必須有一種方法強制進行。例如:如果2個線程想要通信並且要共享一個復雜的數據結構,如鏈表,此時需要
確保它們互不沖突,也就是必須阻止B線程在A線程讀數據的過程中向鏈表裡面寫數據(A獲得了鎖,B必須等A釋放了該鎖)。

為了達到這個目的,java在一個舊的的進程同步模型——監控器(Monitor)的基礎上實現了一個巧妙的方案:監控器是一個控制機制,可以認為是一個
很小的、只能容納一個線程的盒子,一旦一個線程進入監控器,其它的線程必須等待,直到那個線程退出監控為止。通過這種方式,一個監控器可以保證共享資源在
同一時刻只可被一個線程使用。這種方式稱之為同步。(一旦一個線程進入一個實例的任何同步方法,別的線程將不能進入該同一實例的其它同步方法,但是該實例
的非同步方法仍然能夠被調用)。

錯誤的理解:同步嘛,就是幾個線程可以同時進行訪問。

同步和多線程關系:沒多線程環境就不需要同步;有多線程環境也不一定需要同步。

鎖提供了兩種主要特性:互斥(mutual exclusion) 和可見性(visibility)。

互斥即一次只允許一個線程持有某個特定的鎖,因此可使用該特性實現對共享數據的協調訪問協議,這樣,一次就只有一個線程能夠使用該共享數據。

可見性要更加復雜一些,documents它必須確保釋放鎖之前對共享數據做出的更改對於隨後獲得該鎖的另一個線程是可見的 —— 如果沒有同步機制提供的這種可見性保證,線程看到的共享變數可能是修改前的值或不一致的值,這將引發許多嚴重問題

小結:為了防止多個線程並發對同一數據的修改,所以需要同步,否則會造成數據不一致(就是所謂的:線程安全。如java集合框架中Hashtable和
Vector是線程安全的。我們的大部分程序都不是線程安全的,因為沒有進行同步,而且我們沒有必要,因為大部分情況根本沒有多線程環境)。

2、 什麼叫原子的(原子操作)?

Java原子操作是指:不會被打斷地的操作。(就是做到互斥 和可見性?!)

那難道原子操作就可以真的達到線程安全同步效果了嗎?實際上有一些原子操作不一定是線程安全的。

那麼,原子操作在什麼情況下不是線程安全的呢?也許是這個原因導致的:java線程允許線程在自己的內存區保存變數的副本。允許線程使用本地的私有拷貝進
行工作而非每次都使用主存的值是為了提高性能(本人愚見:雖然原子操作是線程安全的,可各線程在得到變數(讀操作)後,就是各自玩
弄自己的副本了,更新操作(寫操作)因未寫入主存中,導致其它線程不可見)。

那該如何解決呢?因此需要通過java同步機制。

在java中,32位或者更少位數的賦值是原子的。在一個32位的硬體平台上,除了double和long型的其它原始類型通常都
是使用32位進行表示,而double和long通常使用64位表示。另外,對象引用使用本機指針實現,通常也是32位的。對這些32位的類型的操作是原
子的。

這些原始類型通常使用32位或者64位表示,這又引入了另一個小小的神話:原始類型的大小是由語言保證的。這是不對的。java語言保證的是原始類型的表
數范圍而非JVM中的存儲大小。因此,int型總是有相同的表數范圍。在一個JVM上可能使用32位實現,而在另一個JVM上可能是64位的。在此再次強
調:在所有平台上被保證的是表數范圍,32位以及更小的值的操作是原子的。

3、 不要搞混了:同步、非同步

舉個例子:普通B/S模式(同步)AJAX技術(非同步)

同步:提交請求->等待伺服器處理->處理完返回 這個期間客戶端瀏覽器不能幹任何事

非同步:請求通過事件觸發->伺服器處理(這是瀏覽器仍然可以作其他事情)->處理完畢

可見,彼「同步」非此「同步」——我們說的java中的那個共享數據同步(synchronized)

一個同步的對象是指行為(動作),一個是同步的對象是指物質(共享數據)。

4、 Java同步機制有4種實現方式:(部分引用網上資源)

① ThreadLocal ② synchronized( ) ③ wait() 與 notify() ④ volatile

目的:都是為了解決多線程中的對同一變數的訪問沖突
ThreadLocal
ThreadLocal 保證不同線程擁有不同實例,相同線程一定擁有相同的實例,即為每一個使用該變數的線程提供一個該變數值的副本,每一個線程都可以獨立改變自己的副本,而不是與其它線程的副本沖突。

優勢:提供了線程安全的共享對象

與其它同步機制的區別:同步機制是為了同步多個線程對相同資源的並發訪問,是為了多個線程之間進行通信;而 ThreadLocal 是隔離多個線程的數據共享,從根本上就不在多個線程之間共享資源,這樣當然不需要多個線程進行同步了。

volatile
volatile 修飾的成員變數在每次被線程訪問時,都強迫從共享內存中重讀該成員變數的值。而且,當成員變數發生變化時,強迫線程將變化值回寫到共享內存。
優勢:這樣在任何時刻,兩個不同的線程總是看到某個成員變數的同一個值。
緣由:Java
語言規范中指出,為了獲得最佳速度,允許線程保存共享成員變數的私有拷貝,而且只當線程進入或者離開同步代碼塊時才與共享成員變數的原
始值對比。這樣當多個線程同時與某個對象交互時,就必須要注意到要讓線程及時的得到共享成員變數的變化。而 volatile
關鍵字就是提示 VM :對於這個成員變數不能保存它的私有拷貝,而應直接與共享成員變數交互。
使用技巧:在兩個或者更多的線程訪問的成員變數上使用 volatile 。當要訪問的變數已在 synchronized 代碼塊中,或者為常量時,不必使用。

線程為了提高效率,將某成員變數(如A)拷貝了一份(如B),線程中對A的訪問其實訪問的是B。只在某些動作時才進行A和B的同步,因此存在A和B不一致
的情況。volatile就是用來避免這種情況的。
volatile告訴jvm,它所修飾的變數不保留拷貝,直接訪問主內存中的(讀操作多時使用較好;線程間需要通信,本條做不到)

Volatile 變數具有 synchronized 的可見性特性,但是不具備原子特性。這就是說線程能夠自動發現 volatile
變數的最新值。Volatile
變數可用於提供線程安全,但是只能應用於非常有限的一組用例:多個變數之間或者某個變數的當前值與修改後值
之間沒有約束。

您只能在有限的一些情形下使用 volatile 變數替代鎖。要使 volatile 變數提供理想的線程安全,必須同時滿足下面兩個條件:

對變數的寫操作不依賴於當前值;該變數沒有包含在具有其他變數的不變式中。

sleep() vs wait()
sleep是線程類(Thread)的方法,導致此線程暫停執行指定時間,把執行機會給其他線程,但是監控狀態依然保持,到時後會自動恢復。調用sleep不會釋放對象鎖。
wait是Object類的方法,對此對象調用wait方法導致本線程放棄對象鎖,進入等待此對象的等待鎖定池,只有針對此對象發出notify方法(或notifyAll)後本線程才進入對象鎖定池准備獲得對象鎖進入運行狀態。

(如果變數被聲明為volatile,在每次訪問時都會和主存一致;如果變數在同步方法或者同步塊中被訪問,當在方法或者塊的入口處獲得鎖以及方法或者塊退出時釋放鎖時變數被同步。)

㈡ java多線程並發去調用一個類的靜態方法,會有問題嗎

總的結論:java是線程安全的,即對任何方法(包括靜態方法)都可以不考慮線程沖突,但有一個前提,就是不能存在全局變數。如果存在全局變數,則需要使用同步機制。

如下通過一組對比例子從頭講解:
在多線程中使用靜態方法會發生什麼事?也就是說多線程訪問同一個類的static靜態方法會發生什麼事?是否會發生線程安全問題?
public class Test {
public static void operation(){
// ... do something
}
}
事實證明只要在靜態函數中沒有處理多線程共享數據,就不存在著多線程訪問同一個靜態方法會出現資源沖突的問題。下面看一個例子:
public class StaticThread implements Runnable {
@Override
public void run() {
// TODO Auto-generated method stub
StaticAction.print();
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(new StaticThread()).start();
}
}
}
public class StaticAction {
public static int i = 0;
public static void print() {
int sum = 0;
for (int i = 0; i < 10; i++) {
System.out.print("step " + i + " is running.");
sum += i;
}
if (sum != 45) {
System.out.println("Thread error!");
System.exit(0);
}
System.out.println("sum is " + sum);
}
}
實際執行的結果顯示各個線程對靜態方法的訪問是交叉執行的,但是這並不影響各個線程靜態方法print()中sum值的計算。也就是說,在此過程中沒有使用全局變數的靜態方法在多線程中是安全的,靜態方法是否引起線程安全問題主要看該靜態方法是否對全局變數(靜態變數static member)進行修改操作。
在多線程中使用同一個靜態方法時,每個線程使用各自的實例欄位(instance field)的副本,而共享一個靜態欄位(static field)。所以說,如果該靜態方法不去操作一個靜態成員,只在方法內部使用實例欄位(instance field),不會引起安全性問題。
但是,如果該靜態方法操作了一個靜態變數,則需要靜態方法中採用互斥訪問的方式進行安全處理。我們來看一下沒有使用互斥訪問的話會產生怎樣的問題:public class StaticAction {
public static int i = 0;
public static void incValue() {
int temp = StaticAction.i;
try {
Thread.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
temp++;
StaticAction.i = temp;
}
}
public class StaticThread implements Runnable {
@Override
public void run() {
// TODO Auto-generated method stub
StaticAction.incValue();
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(new StaticThread()).start();
}
try {
Thread.sleep(1000); //預留足夠的時間讓上面的線程跑完
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(StaticAction.i);
}
}
實際運行結果顯示i值為隨機的數字。為了實現互斥訪問,這時我們需要加入一個synchronized關鍵字。代碼修改如下:
public class StaticAction {
public static int i = 0;
public synchronized static void incValue() {
int temp = StaticAction.i;
try {
Thread.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
temp++;
StaticAction.i = temp;
}
}
public class StaticThread implements Runnable {
@Override
public void run() {
// TODO Auto-generated method stub
StaticAction.incValue();
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(new StaticThread()).start();
}
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(StaticAction.i);
}
}
運行結果則必然是100。
加入synchronized關鍵字的靜態方法稱為同步靜態方法。
在訪問同步靜態方法時,會獲取該類的「Class」對象,所以當一個線程進入同步的靜態方法中時,線程監視器獲取類本身的對象鎖,其它線程不能進入這個類的任何靜態同步方法。它不像實例方法,因為多個線程可以同時訪問不同實例同步實例方法。這個其實就是操作系統中的用信號量實現進程的互斥與同步問題,如果涉及在同一個類中有多個靜態方法中處理多線程共享數據的話,那就變成用信號量解決生產者-消費者問題。也就是說,靜態方法是一份臨界資源,對靜態方法的訪問屬於進入臨界區;對靜態變數的修改是一份臨界資源,對靜態變數的修改屬於進入臨界區。

㈢ Java語言有哪些特點

面向對象:其實是現實世界模型的自然延伸。現實世界中任何實體都可以看作是對象。對象之間通過消息相互作用。另外,現實世界中任何實體都可歸屬於某類事物,任何對象都是某一類事物的實例。如果說傳統的過程式編程語言是以過程為中心以演算法為驅動的話,面向對象的編程語言則是以對象為中心以消息為驅動。用公式表示,過程式編程語言為:程序=演算法+數據;面向對象編程語言為:程序=對象+消息。 所有面向對象編程語言都支持三個概念:封裝、多態性和繼承,Java也不例外。現實世界中的對象均有屬性和行為,映射到計算機程序上,屬性則表示對象的數據,行為表示對象的方法(其作用是處理數據或同外界交互)。所謂封裝,就是用一個自主式框架把對象的數據和方法聯在一起形成一個整體。可以說,對象是支持封裝的手段,是封裝的基本單位。Java語言的封裝性較強,因為Java無全程變數,無主函數,在Java中絕大部分成員是對象,只有簡單的數字類型、字元類型和布爾類型除外。而對於這些類型,Java也提供了相應的對象類型以便與其他對象交互操作。

可移植性:就是在這個系統上作的程序經過一次編譯後可以移植到別的系統上解釋執行,只要經過簡單的粘貼和復制就行了,不影響程序的效果

安全性:在 iSeries 伺服器上運行的大多數 Java(TM) 程序是應用程序,而不是 applet,所以「砂箱」安全性模型對它們不起限製作用。從安全性的觀點看,Java 應用程序所受的安全性限制與 iSeries 伺服器上的任何其它程序相同。要在 iSeries 伺服器上運行 Java 程序,您必須對集成文件系統中的類文件具有許可權。程序一旦啟動,它就在該用戶許可權控制下運行。 您可以使用沿用許可權來訪問具有運行程序的用戶的許可權和程序擁有者許可權的對象。沿用許可權臨時地將用戶原先無權訪問的對象的許可權授予用戶。

並發性:JAVA支持多線程技術,就是多個線程並行機制,多線程是Java的一個重要方法,特別有利於在程序中實現並發任務.Java提供Thread線程類,實現了多線程的並發機制.然而,程序的並發執行必定會出現多個線程互斥訪問臨界資源的局面,因而並發系統解決的關鍵就是對臨界資源的管理和分配問題,而在進行臨界資源分配時有兩方面需要考慮,即安全性和公平性.文中首先討論了多線程並發系統中的安全性與公平性問題,指出安全性與公平性在並發系統中訪問臨界資源時的重要性.並通過火車行駛單行隧道的實例,演示各種條件下的行駛情況來進一步說明該問題.

可視化:不好說,像vb這樣的也是可視話的編成程序。
我借鑒了一些朋友的答案,還有一些是自己找啊,希望能給你帶來幫助

㈣ JAVA的技術特點是什麼

JAVA的技術特點是什麼?

Java由美國SUN公司(被oracle公司收購)發明於1995年,是目前業界應用最廣泛、使用人數最多的語言,連續多年排名世界第一,可以稱之為「計算機語言界的英語」。

那麼Java這么高的評價它的特點有哪些,下面我來為題主解答:

一、跨平台/可移植性

這是Java的核心優勢。Java在設計時就很注重移植和跨平台性。比如:Java的int永遠都是32位。不像C++可能是16,32,可能是根據編譯器廠商規定的變化。這樣的話程序的移植就會非常麻煩。

Java首先利用文本編輯器編寫 Java源程序,源文件的後綴名為.java;再利用編譯器(javac)將源程序編譯成位元組碼文件,位元組碼文件的後綴名為.class; 最後利用虛擬機(解釋器,java)解釋執行。如下圖所示:

二、安全性

Java適合於網路/分布式環境,為了達到這個目標,在安全性方面投入了很大的精力,使Java可以很容易構建防病毒,防篡改的系統。比如Java取消了強大但又危險的指針。由於指針可進行移動運算,指針可隨便指向一個內存區域,而不管這個區域是否可用,這樣做是危險的。

三、面向對象

面向對象是一種程序設計技術,非常適合大型軟體的設計和開發。由於C++為了照顧大量C語言使用者而兼容了C,使得自身僅僅成為了帶類的C語言,多少影響了其面向對象的徹底性!Java則是完全的面向對象語言。

四、簡單性

Java就是C++語法的簡化版,我們也可以將Java稱之為「C++-」。跟我念「C加加減」,指的就是將C++的一些內容去掉;比如:頭文件,指針運算,結構,聯合,操作符重載,虛基類等等。同時,由於語法基於C語言,因此學習起來完全不費力。

五、高性能

Java最初發展階段,總是被人詬病「性能低」;客觀上,高級語言運行效率總是低於低級語言的,這個無法避免。Java語言本身發展中通過虛擬機的優化提升了幾十倍運行效率。比如,通過JIT(JUST IN TIME)即時編譯技術提高運行效率。 將一些「熱點」位元組碼編譯成本地機器碼,並將結果緩存起來,在需要的時候重新調用。這樣的話,使Java程序的執行效率大大提高,某些代碼甚至接近C++的效率。

因此,Java低性能的短腿,已經被完全解決了。業界發展上,我們也看到很多C++應用轉到Java開發,很多C++程序員轉型為Java程序員。

六、分布式

Java是為Internet的分布式環境設計的,因為它能夠處理TCP/IP協議。事實上,通過URL訪問一個網路資源和訪問本地文件是一樣簡單的。Java還支持遠程方法調用(RMI,Remote Method Invocation),使程序能夠通過網路調用方法。

七、多線程

多線程的使用可以帶來更好的交互響應和實時行為。 Java多線程的簡單性是Java成為主流伺服器端開發語言的主要原因之一。

八、健壯性

Java是一種健壯的語言,吸收了C/C++ 語言的優點,但去掉了其影響程序健壯性的部分(如:指針、內存的申請與釋放等)。Java程序不可能造成計算機崩潰。即使Java程序也可能有錯誤。如果出現某種出乎意料之事,程序也不會崩潰,而是把該異常拋出,再通過異常處理機制加以處理。

以上幾種特性你了解到啦嗎? 記得給個贊!

當然這些還沒完,Java除啦這些特性還有一些其他的特點我也來給你講一講!!

1.java的風格類似C++,從某種意義上講,java是C++的一個變種。但是java摒棄了C、C++中的容易引發錯誤和難以理解的指針,結構,以及內存管理等;

2.java是一種強類型的語言,比C/C++檢查還要嚴格,java區分大小寫字母;

3.java禁止非法訪問內存,因為java沒有指針,內存的申請和釋放;

4.對於不同的操作系統,會有不同的java虛擬機,這也就決定了java的可移植性;

5.java提供了自動垃圾回收機制gc,有效避免了C、C++的內存泄漏問題;

記得給個贊!

㈤ Java如何實現線程之間的互斥

  • 臨界區(Critical Section):適合一個進程內的多線程訪問公共區域或代碼段時使用

  • Java如何實現線程之間的互斥

  • 互斥量 (Mutex):適合不同進程內多線程訪問公共區域或代碼段時使用,與臨界區相似。

  • 事件(Event):通過線程間觸發事件實現同步互斥

  • 信號量(Semaphore):與臨界區和互斥量不同,可以實現多個線程同時訪問公共區域數據,原理與操作系統中PV操作類似,先設置一個訪問公共區域的線程最大連接數,每有一個線程訪問共享區資源數就減一,直到資源數小於等於零。

㈥ JAVA中線程在什麼時候需要同步和互斥

何時需要同步
在多個線程同時訪問互斥(可交換)數據時,應該同步以保護數據,確保兩個線程不會同時修改更改它。
對於非靜態欄位中可更改的數據,通常使用非靜態方法訪問
對於靜態欄位中可更改的數據,通常使用靜態方法訪問。

1、線程同步的目的是為了保護多個線程反問一個資源時對資源的破壞。
2、線程同步方法是通過鎖來實現,每個對象都有切僅有一個鎖,這個鎖與一個特定的對象關聯,線程一旦獲取了對象鎖,其他訪問該對象的線程就無法再訪問該對象的其他非同步方法。
3、對於靜態同步方法,鎖是針對這個類的,鎖對象是該類的Class對象。靜態和非靜態方法的鎖互不幹預。一個線程獲得鎖,當在一個同步方法中訪問另外對象上的同步方法時,會獲取這兩個對象鎖。
4、對於同步,要時刻清醒在哪個對象上同步,這是關鍵。
5、編寫線程安全的類,需要時刻注意對多個線程競爭訪問資源的邏輯和安全做出正確的判斷,對「原子」操作做出分析,並保證原子操作期間別的線程無法訪問競爭資源。
6、當多個線程等待一個對象鎖時,沒有獲取到鎖的線程將發生阻塞。
7、死鎖是線程間相互等待鎖鎖造成的,在實際中發生的概率非常的小。真讓你寫個死鎖程序,不一定好使,呵呵。但是,一旦程序發生死鎖,程序將死掉。

使用鎖定還有一些其他危險,如死鎖(當以不一致的順序獲得多個鎖定時會發生死鎖)。甚至沒有這種危險,鎖定也僅是相對的粗粒度協調機制,同樣非常適合管理簡單操作,如增加計數器或更新互斥擁有者。如果有更細粒度的機制來可靠管理對單獨變數的並發更新,則會更好一些;在大多數現代處理器都有這種機制。

㈦ 雲南北大青鳥java培訓告訴你關於線程安全問題分析

在學習java編程開發語言的過程中,我們掌握了線程與線程池等相關技術知識。今天,北大青鳥麗江計算機學院http://www.kmbdqn.cn/就關於線程安全問題給大家做一個簡單的說明和介紹,一起來了解一下吧。


線程安全就是多線程訪問時,採用了加鎖機制,當一個線程訪問該類的某個數據時,進行保護,其他線程不能進行訪問直到該線程讀取完,其他線程才可使用。不會出現數據不一致或者數據污染。


線程不安全就是不提供數據訪問保護,有可能出現多個線程先後更改數據造成所得到的數據是臟數據。


什麼時候考慮到線程安全:


一個對象是否需要線程安全,取決於該對象是否被多線程訪問。這指的是程序中訪問對象的方式,而不是對象要實現的功能。要使得對象是線程安全的,要採用同步機制來協同對對象可變狀態的訪問。Java常用的同步機制是Synchronized,還包括volatile類型的變數,顯示鎖以及原子變數。在多個線程中,當它們同時訪問同個類時,每次執行的結果和單線程結果一致,且變數值跟預期一致,這個類則是線程安全的。



鎖的特性


鎖機制的兩種特性:


互斥性:即同一時間只允許一個線程持有某個對象的鎖,通過這種特性來實現多線程中的協調機制,這樣在同一時間只有一個線程對需同步的代碼塊(復合操作)進行訪問。互斥性我們也往往稱為操作的原子性。


可見性:必須確保在鎖被釋放之前,對共享變數所做的修改,對於隨後獲得該鎖的另一個線程是可見的,否則另一個線程可能是在本地緩存的某個副本上繼續操作從而引起不一致。


掛起、休眠、阻塞和非阻塞


掛起:當線程被掛起時,其會失去CPU的使用時間,直到被其他線程(用戶線程或調試線程)喚醒。


休眠:同樣是會失去CPU的使用時間,但是在過了指定的休眠時間之後,它會自動激活,無需喚醒(整個喚醒表面看是自動的,但實際上也得有守護線程去喚醒,只是不需編程者手動干預)。


阻塞:在線程執行時,所需要的資源不能得到,則線程被掛起,直到滿足可操作的條件。


非阻塞:在線程執行時,所需要的資源不能得到,則線程不是被掛起等待,而是繼續執行其餘事情,等待條件滿足了後,收到了通知(同樣是守護線程去做)再執行。


㈧ Java中如何保證線程安全性

線程安全主要在體現在這三個方面:
1.原子性:提供互斥訪問,同一時刻只能有一個線程對數據進行操作,(atomic,synchronized);
2.可見性:一個線程對主內存的修改可以及時地被其他線程看到,(synchronized,volatile);
3.有序性:一個線程觀察其他線程中的指令執行順序,由於指令重排序,該觀察結果一般雜亂無序,(happens-before原則)。
所以想保證線性安全的話只要從這三個方面入手就可以了。

㈨ java並發編程中,有哪些同步和互斥機制

多線程共享資源,比如一個對象的內存,怎樣保證多個線程不會同時訪問(讀取或寫入)這個對象,這就是並發最大的難題,因此產生了 互斥機制(鎖)。
using the same monitor lock.

獲取鎖後,該線程本地存儲失效,臨界區(就是獲得鎖後釋放鎖之前 的代碼區)從主存獲取數據,並在釋放鎖後刷入主存。
互斥:
保證臨界區代碼線程間互斥。
synchronized實現同步的基礎:
java中每個對象都可以作為鎖

一個任務可以多次獲得鎖,比如在一個線程中調用一個對象的 synchronized標記的方法,在這個方法中調用第二個synchronized標記的方法,然後在第二個synchronized方法中調用第三個synchronized方法。一個線程每次進入一個synchronized方法中JVM都會跟蹤加鎖的次數,每次+1,當該這個方法執行完畢,JVM計數-1;當JVM計數為0時,鎖完全被釋放,其他線程可以訪問該變數。
在使用並發時將對象的field設為private 很重要!尤其是使用static變數(evil static variable) 使用 Lock lock =new ReentrantLock()的問題是代碼不夠優雅,增加代碼量;我們一般都是使用synchronized實現互斥機制。但是1.當代碼中拋出異常時,顯示鎖的finally里可以進行資源清理工作。2.ReentrantLock還給我們更細粒度的控制力

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

熱點內容
隨機啟動腳本 發布:2025-07-05 16:10:30 瀏覽:535
微博資料庫設計 發布:2025-07-05 15:30:55 瀏覽:31
linux485 發布:2025-07-05 14:38:28 瀏覽:310
php用的軟體 發布:2025-07-05 14:06:22 瀏覽:760
沒有許可權訪問計算機 發布:2025-07-05 13:29:11 瀏覽:436
javaweb開發教程視頻教程 發布:2025-07-05 13:24:41 瀏覽:723
康師傅控流腳本破解 發布:2025-07-05 13:17:27 瀏覽:246
java的開發流程 發布:2025-07-05 12:45:11 瀏覽:696
怎麼看內存卡配置 發布:2025-07-05 12:29:19 瀏覽:288
訪問學者英文個人簡歷 發布:2025-07-05 12:29:17 瀏覽:837