本文目錄一覽:
- 1、Java多線程同步的幾種方式
- 2、java多線程有幾種實現方法,都是什麼?同步有幾種實現方法,都是什麼?
- 3、java 實現線程同步的方式有哪些
- 4、Java多線程初學者指南(12):使用Synchronized塊同步變量
- 5、java多線程開發的同步機制有哪些
- 6、java中線程同步的幾種方法
Java多線程同步的幾種方式
java中多線程的實現方法有兩種:1.直接繼承thread類;2.實現runnable接口;同步的實現方法有五種:1.同步方法;2.同步代碼塊;3.使用特殊域變量(volatile)實現線程同步;4.使用重入鎖實現線程同步;5.使用局部變量實現線程同步 。
其中多線程實現過程中需注意重寫或者覆蓋run()方法,而對於同步的實現方法中使用較常使用的是利用synchronized編寫同步方法和代碼塊。
謝謝採納!!
java多線程有幾種實現方法,都是什麼?同步有幾種實現方法,都是什麼?
java中多線程的實現方法有兩種:1.直接繼承thread類;2.實現runnable接口;同步的實現方法有五種:1.同步方法;2.同步代碼塊;3.使用特殊域變量(volatile)實現線程同步;4.使用重入鎖實現線程同步;5.使用局部變量實現線程同步
。
其中多線程實現過程中需注意重寫或者覆蓋run()方法,而對於同步的實現方法中使用較常使用的是利用synchronized編寫同步方法和代碼塊。
java 實現線程同步的方式有哪些
實現同步機制有兩個方法:
1、同步代碼塊:
synchronized(同一個數據){} 同一個數據:就是N條線程同時訪問一個數據。
2、同步方法:
public synchronized 數據返回類型 方法名(){}
就是使用 synchronized 來修飾某個方法,則該方法稱為同步方法。對於同步方法而言,無需顯示指定同步監視器,同步方法的同步監視器是 this 也就是該對象的本身(這裡指的對象本身有點含糊,其實就是調用該同步方法的對象)通過使用同步方法,可非常方便的將某類變成線程安全的類,具有如下特徵:
1,該類的對象可以被多個線程安全的訪問。
2,每個線程調用該對象的任意方法之後,都將得到正確的結果。
3,每個線程調用該對象的任意方法之後,該對象狀態依然保持合理狀態。
註:synchronized關鍵字可以修飾方法,也可以修飾代碼塊,但不能修飾構造器,屬性等。
實現同步機制注意以下幾點: 安全性高,性能低,在多線程用。性能高,安全性低,在單線程用。
1,不要對線程安全類的所有方法都進行同步,只對那些會改變共享資源方法的進行同步。
2,如果可變類有兩種運行環境,當線程環境和多線程環境則應該為該可變類提供兩種版本:線程安全版本和線程不安全版本(沒有同步方法和同步塊)。在單線程中環境中,使用線程不安全版本以保證性能,在多線程中使用線程安全版本.
Java多線程初學者指南(12):使用Synchronized塊同步變量
我們可以通過synchronized塊來同步特定的靜態或非靜態方法 要想實現這種需求必須為這些特性的方法定義一個類變量 然後將這些方法的代碼用synchronized塊括起來 並將這個類變量作為參數傳入synchronized塊 下面的代碼演示了如何同步特定的類方法
package mythread; public class SyncThread extends Thread { private static String sync = ; private String methodType = ; private static void method(String s) { synchronized (sync) { sync = s; System out println(s); while (true); } } public void method () { method( method ); } public static void staticMethod () { method( staticMethod ); } public void run() { if (methodType equals( static )) staticMethod (); else if (methodType equals( nonstatic )) method (); } public SyncThread(String methodType) { thodType = methodType; } public static void main(String[] args) throws Exception { SyncThread sample = new SyncThread( nonstatic ); SyncThread sample = new SyncThread( static ); sample start(); sample start(); } }
運行結果如下
method staticMethod
看到上面的運行結果很多讀者可能感到驚奇 在上面的代碼中method 和staticMethod 方法使用了靜態字符串變量sync進行同步 這兩個方法只能有一個同時執行 而這兩個方法都會執行 行的無限循環語句 因此 輸出結果只能是method 和staticMethod 其中之一 但這個程序將這兩個字符串都輸出了
出現這種結果的願意很簡單 我們看一下 行就知道了 原來在這一行將sync的值改變了 在這裡要說一下Java中的String類型 String類型和Java中其他的複雜類型不同 在使用String型變量時 只要給這個變量賦一次值 Java就會創建個新的String類型的實例 如下面的代碼所示
String s = hello ;System out println(s hashCode());s = world ;System out println(s hashCode());
在上面的代碼中 第一個s和再次賦值後的s的hashCode的值是不一樣的 由於創建String類的實例並不需要使用new 因此 在同步String類型的變量時要注意不要給這個變量賦值 否則會使變量無法同步
由於在 行已經為sync創建了一個新的實例 假設method 先執行 當method 方法執行了 行的代碼後 sync的值就已經不是最初那個值了 而method 方法鎖定的仍然是sync變量最初的那個值 而在這時 staticMethod 正好執行到synchronized(sync) 在staticMethod 方法中要鎖定的這個sync和method 方法鎖定的sync已經不是一個了 因此 這兩個方法的同步性已經被破壞了
解決以上問題的方法當然是將 行去掉 在本例中加上這行 只是為了說明使用類變量來同步方法時如果在synchronized塊中將同步變量的值改變 就會破壞方法之間的同步 為了徹底避免這種情況發生 在定義同步變量時可以使用final關鍵字 如將上面的程序中的 行可改成如下形式
private final static String sync = ;
使用final關鍵字後 sync只能在定義時為其賦值 並且以後不能再修改 如果在程序的其他地方給sync賦了值 程序就無法編譯通過 在Eclipse等開發工具中 會直接在錯誤的地方給出提示
我們可以從兩個角度來理解synchronized塊 如果從類方法的角度來理解 可以通過類變量來同步相應的方法 如果從類變量的角度來理解 可以使用synchronized塊來保證某個類變量同時只能被一個方法訪問 不管從哪個角度來理解 它們的實質都是一樣的 就是利用類變量來獲得同步鎖 通過同步鎖的互斥性來實現同步
lishixinzhi/Article/program/Java/gj/201311/27400
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中線程同步的幾種方法
線程同步主要有以下種方法(示例中是實現計數的功能):
1、同步方法,即使用synchronized關鍵字修飾方法,例如:
public synchronized void add(int c){…}
2、同步代碼塊,即有synchronized關鍵字修飾的語句塊,例如:
public void addAndGet(int c){
synchronized(this){
count += c;
}
}
3、使用特殊域變量(volatile)實現線程同步,該方法不能保證絕對的同步。
例如:private volatile int count = 0;
4、使用鎖實現線程同步,例如:
private Lock lock = new ReentrantLock();
public void add(int c) {
lock.lock();//上鎖
try{
count += c;
}finally{
lock.unlock();//解鎖
}
}
5、使用原子變量實現線程同步,在java的util.concurrent.atomic包中提供了創建了原子類型變量的工具類,例如:
private AtomicInteger count= new AtomicInteger(1);
public void add(int c) {
count.addAndGet(c);
}
6、使用局部變量實現線程同步,如果使用ThreadLocal管理變量,則每一個使用該變量的線程都獲得該變量的副本, 副本之間相互獨立,這樣每一個線程都可以隨意修改自己的變量副本,而不會對其他線程產生影響。
ThreadLocal 類的常用方法
new ThreadLocalT() : 創建一個線程本地變量
get() : 返回此線程局部變量的當前線程副本中的值
initialValue() : 返回此線程局部變量的當前線程的”初始值”
set(T value) : 將此線程局部變量的當前線程副本中的值設置為value
示例代碼:
private static ThreadLocalInteger count= new ThreadLocalInteger(){
@Override
protected Integer initialValue(){
return 1;
}
};
public void add(int c){
count.set(count.get() + c);
}
7、使用阻塞隊列實現,例如LinkedBlockingQueue,具體使用可百度LinkedBlockingQueue的用法或查看java文檔。
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/293359.html