【併發技術03】傳統執行緒互斥技術—synchronized
在多個執行緒同時操作相同資源的時候,就會遇到併發的問題,如銀行轉賬啊、售票系統啊等。為了避免這些問題的出現,我們可以使用 synchronized 關鍵字來解決,下面針對 synchronized 常見的用法做一個總結。首先寫一個存在併發問題的程式,如下:
public class TraditionalThreadSynchronized { public static void main(String[] args) { //在靜態方法中不能new內部類的例項物件 //private Outputer outputer = new Outputer(); new TraditionalThreadSynchronized().init(); } private void init() { final Outputer outputer = new Outputer(); //執行緒1列印:duoxiancheng new Thread(new Runnable() { @Override public void run() { while(true) { try { Thread.sleep(5); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } outputer.output1("duoxiancheng"); } } }).start();; //執行緒2列印:eson15 new Thread(new Runnable() { @Override public void run() { while(true) { try { Thread.sleep(5); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } outputer.output1("eson15"); } } }).start();; } static class Outputer { //自定義一個字串列印方法,一個個字元的列印 public void output1(String name) { int len = name.length(); for(int i = 0; i < len; i++) { System.out.print(name.charAt(i)); } System.out.println(""); } } }
在內部類 Outputer 中定義了一個列印字串的方法,一個字元一個字元的列印,這樣比較容易直觀的看出併發問題,因為字元順序打亂了就說明出現問題了。然後在 init 方法中開啟兩個執行緒,一個執行緒列印 “duoxiancheng” ,另一個執行緒列印 “eson15”。看一下執行結果:
eson15duoxianche
ng
eson15
duoxiancheng
duoxiancheng
eson15
esduoxiancheng
on15
duoxiancheng
從輸出的結果中可以看到,已經出現問題了,為了解決這個問題,可以使用 synchronized 同步程式碼塊來對公共部分進行同步操作,但是需要給它一把鎖,這把鎖是一個物件,可以是任意一個物件,但是前提是,兩個執行緒使用的必須是同一個物件鎖才可以,這很好理解。那麼下面在
output1
()
方法中加入 synchronized 程式碼塊:
static class Outputer { private String token = ""; //定義一個鎖 public void output1(String name) { synchronized(token) //任何一個物件都可以作為引數,但是該物件對於兩個執行緒來說是同一個才行 //如果用name就不行了,因為不同的執行緒進來name是不一樣的,不是同一個name { int len = name.length(); for(int i = 0; i < len; i++) { System.out.print(name.charAt(i)); } System.out.println(""); } } }
經過上面的改造,執行緒安全問題就基本解決了,但是還可以再往下引申,如果在方法上加 synchronized 關鍵字的話,那麼這個同步鎖是什麼呢?我們在 Outputer 類中再寫一個
output2
()
方法:
static class Outputer { private String token = ""; //定義一個鎖 public void output1(String name) { synchronized(token) //任何一個物件都可以作為引數,但是該物件對於兩個執行緒來說是同一個才行 { int len = name.length(); for(int i = 0; i < len; i++) { System.out.print(name.charAt(i)); } System.out.println(""); } } public synchronized void output2(String name) { int len = name.length(); for(int i = 0; i < len; i++) { System.out.print(name.charAt(i)); } System.out.println(""); } }
方法內部實現邏輯一模一樣,唯一不同的就是 synchronized 加在了方法上,那麼我們讓
init
()
方法中的兩個執行緒中,一個呼叫
output1
(
String
name
)
方法,另一個呼叫
output2
(
String
name
)
方法,從結果中能看出,執行緒安全問題又出現了。產生問題的原因不難發現:現在兩個方法都加了 synchronized,但是兩個執行緒在呼叫兩個不同的方法還是出現了問題,也就是說,還是各玩各的……那麼問題就出在這個鎖上,說明兩者並沒有使用同一把鎖!
如果我們把
output1
()
方法中 synchronized 中的 token 改成 this,再執行就沒問題了,這說明一點:
synchronized 關鍵字修飾方法的時候,同步鎖是 this,即等效於程式碼塊
synchronized
(
this
)
{...}
。
再繼續往下引申,現在在 Outputer 類中再寫一個靜態方法
output3
(
String
name
)
,並且也讓 synchronized 去修飾這個靜態方法。
static class Outputer { private String token = ""; //定義一個鎖 public void output1(String name) { synchronized(token) //任何一個物件都可以作為引數,但是該物件對於兩個執行緒來說是同一個才行 { int len = name.length(); for(int i = 0; i < len; i++) { System.out.print(name.charAt(i)); } System.out.println(""); } } public static synchronized void output3(String name) { int len = name.length(); for(int i = 0; i < len; i++) { System.out.print(name.charAt(i)); } System.out.println(""); } } }
然後在
init
()
方法中一個執行緒呼叫
output1
()
方法,另一個執行緒呼叫
output3
()
方法。毫無疑問,肯定又會出現執行緒安全問題。但是如何解決呢?因為 static 方法在類載入的時候就載入了,所以這個鎖應該是類的位元組碼物件。那麼將 token 改為
Outputer
.
class
就解決問題了,這說明一點:
synchronized 關鍵字修飾 static 方法的時候,同步鎖是該類的位元組碼物件,即等效於程式碼塊
synchronized
(
classname
.
class
)
{...}
。
最後再總結一下:
同步程式碼塊的鎖是任意物件 。只要不同的執行緒都執行同一個同步程式碼塊的時候,這個鎖隨便設。
同步函式的鎖是固定的 this 。當需要和同步函式中的邏輯實行同步的時候,程式碼塊中的鎖必須為 this。
靜態同步函式的鎖是該函式所屬類的位元組碼檔案物件 。該物件可以用
this . getClass ()
方法獲取,也可以使用當前類名. class
表示。
如果覺得對您有幫助,轉發給更多人吧~
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31558358/viewspace-2217376/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Java併發基礎03:傳統執行緒的互斥技術—synchronizedJava執行緒synchronized
- 【併發技術02】傳統執行緒技術中的定時器技術執行緒定時器
- Java併發技術05:傳統執行緒同步通訊技術Java執行緒
- 【併發技術01】傳統執行緒技術中建立執行緒的兩種方式執行緒
- Java併發基礎02:傳統執行緒技術中的定時器技術Java執行緒定時器
- 【併發技術04】執行緒技術之死鎖問題執行緒
- 多執行緒與併發-----Lock鎖技術執行緒
- Java併發基礎01:揭祕傳統執行緒技術中建立執行緒的兩種方式Java執行緒
- Java併發基礎04:執行緒技術之死鎖問題Java執行緒
- 前端開發技術-剖析JavaScript單執行緒前端JavaScript執行緒
- 五、併發控制(1):執行緒的互斥執行緒
- java核心技術筆記--執行緒Java筆記執行緒
- 保證執行緒安全的技術執行緒
- 多執行緒核心技術(1)-執行緒的基本方法執行緒
- 高併發技術
- 前端開發技術-剖析JavaScript單執行緒 原創前端JavaScript執行緒
- 一執行緒序員忙著學習技術,二執行緒序員忙著技術變現,你呢?執行緒
- iOS 多執行緒的四種技術方案iOS執行緒
- 技術分享 | DLL注入之遠執行緒注入執行緒
- GO-併發技術Go
- 併發技術中同步
- 多執行緒03:?執行緒傳參詳解執行緒
- openGauss執行器技術
- 併發技術1:CSP併發理論
- 人工智慧技術對傳統技術的演變人工智慧
- 多執行緒與高併發(三)synchronized關鍵字執行緒synchronized
- 執行緒同步與互斥:互斥鎖執行緒
- 《java併發程式設計的藝術》執行緒池Java程式設計執行緒
- 技術問答集錦(12)併發程式設計-任務執行程式設計
- 虛擬執行緒原理及效能分析|得物技術執行緒
- 悲觀的併發策略——synchronized互斥鎖synchronized
- ARM技術 —— 條件執行
- 併發技術4:讀寫鎖
- 併發技術3:定時器定時器
- 併發技術4:同步排程
- 併發技術3:管道通訊
- 併發技術2:多協程
- 高併發設計技術方案