摘要:異步線程運(yùn)行線程運(yùn)行我們創(chuàng)建兩個線程來調(diào)用同一業(yè)務(wù)對象的相同功能時可以看到下面輸出線程運(yùn)行線程運(yùn)行兩個線程在一起執(zhí)行方法并且交叉打印也就是說當(dāng)我們啟動一個線程執(zhí)行某個方法的時候就是異步執(zhí)行至于為啥要這樣演示是因為下面的同步同步將方法上加入關(guān)
異步
public class PrintObject { public void printString(){ System.out.println("begin"); if(Thread.currentThread().getName().equals("a")){ PrintStream printStream = System.out; printStream.println("線程 a 運(yùn)行"); } if(Thread.currentThread().getName().equals("b")){ System.out.println("線程 b 運(yùn)行"); } System.out.println("end"); } }
public static void main(String[] a) { PrintObject pb = new PrintObject(); Thread thread1 = new Thread(pb::printString); thread1.setName("a"); thread1.start(); Thread thread2 = new Thread(pb::printString); thread2.setName("b"); thread2.start(); }
我們創(chuàng)建兩個線程來調(diào)用同一業(yè)務(wù)對象的相同功能時, 可以看到下面輸出.
begin begin 線程 a 運(yùn)行 end 線程 b 運(yùn)行 end
兩個線程在一起執(zhí)行 printString 方法, 并且交叉打印. 也就是說當(dāng)我們啟動一個線程執(zhí)行某個方法的時候就是異步執(zhí)行, 至于為啥要這樣演示, 是因為下面的同步.
同步將 synchronized public void printString() 方法上加入 synchronized 關(guān)鍵字, 來使方法同步.
執(zhí)行結(jié)果:
begin 線程 a 運(yùn)行 end begin 線程 b 運(yùn)行 end
那么為什么加入 synchronized 關(guān)鍵字后就會同步呢? 這是因為關(guān)鍵字 synchronized 會取得一把對象鎖, 而不是把一段代碼或方法當(dāng)做鎖; 哪個線程先執(zhí)行帶 synchronized 關(guān)鍵字的方法, 哪個線程就持有該方法所屬的對象的鎖 Look, 那么其他線程只能呈等待狀態(tài).
這里有個前提是多個線程訪問同一個對象, 下面演示的是多個線程訪問不同的對象.
public class PrintObject { synchronized public void printString(){ System.out.println("begin"); if(Thread.currentThread().getName().equals("a")){ PrintStream printStream = System.out; printStream.println("線程 a 運(yùn)行"); try { Thread.sleep(100000); } catch (InterruptedException e) { } } if(Thread.currentThread().getName().equals("b")){ System.out.println("線程 b 運(yùn)行"); } System.out.println("end"); } }
public static void main(String[] a) { PrintObject pb = new PrintObject(); PrintObject pb1 = new PrintObject(); Thread thread1 = new Thread(pb::printString); thread1.setName("a"); thread1.start(); Thread thread2 = new Thread(pb1::printString); thread2.setName("b"); thread2.start(); }
執(zhí)行結(jié)果
begin 線程 a 運(yùn)行 begin 線程 b 運(yùn)行 end
讓 a 線程睡眠 100000 毫秒, 可以看到 a 線程并沒有執(zhí)行完, b 線程就運(yùn)行了. 這也能夠證明 synchronized 關(guān)鍵字取得是對象鎖.
另外還需要注意一點(diǎn), 我們使用兩個線程執(zhí)行同一對象的不同同步方法時, 如果線程 a 在睡眠, 那么線程 b 也會一直等待, 線程 a 執(zhí)行完畢后再去執(zhí)行.
注: 同步方法一定是線程安全的.synchronized 鎖重入
如果一個獲取鎖的線程調(diào)用其它的synchronized修飾的方法, 會發(fā)生什么?
在一個線程使用synchronized方法時調(diào)用該對象另一個synchronized方法, 即一個線程得到一個對象鎖后再次請求該對象鎖, 是永遠(yuǎn)可以拿到鎖的.
在Java內(nèi)部, 同一個線程調(diào)用自己類中其他synchronized方法/塊時不會阻礙該線程的執(zhí)行, 同一個線程對同一個對象鎖是可重入的, 同一個線程可以獲取同一把鎖多次, 也就是可以多次重入. 原因是Java中線程獲得對象鎖的操作是以線程為單位的, 而不是以調(diào)用為單位的.
這種情況也可以發(fā)生在繼承中, 也就是說子類的同步方法調(diào)用父類的同步方式時, 時可以鎖重入的.synchronized 同步代碼塊
但是, 如果子類重寫了父類的方法, 并沒有使用 synchronized 關(guān)鍵字, 則同步就失效了. 因為子類重寫父類的方法, 當(dāng)我們調(diào)用方法執(zhí)行代碼時, 執(zhí)行的是子類的方法, 所以變成了異步執(zhí)行.
public class PrintObject { public synchronized void printString(){ try { System.out.println(Thread.currentThread().getName() + " 執(zhí)行"); System.out.println(Thread.currentThread().getName() + " 插入數(shù)據(jù)到數(shù)據(jù)庫"); // 讓線程休眠, 模擬出網(wǎng)絡(luò)延時 Thread.sleep(5000); System.out.println(Thread.currentThread().getName() + " 共享數(shù)據(jù)減1"); Thread.sleep(5000); System.out.println(Thread.currentThread().getName() + " 插入數(shù)據(jù)到數(shù)據(jù)庫"); Thread.sleep(5000); if (Thread.currentThread().getName().equals("b")) { SimpleDateFormat df = new SimpleDateFormat("mm:ss"); System.out.println(df.format(new Date())); } } catch (InterruptedException e) { e.printStackTrace(); } } }
PrintObject pb = new PrintObject(); Thread thread1 = new Thread(pb::printString); thread1.setName("a"); Thread thread2 = new Thread(pb::printString); thread2.setName("b"); SimpleDateFormat df = new SimpleDateFormat("mm:ss"); System.out.println(df.format(new Date())); thread1.start(); thread2.start();
執(zhí)行結(jié)果
47:34 a 執(zhí)行 a 插入數(shù)據(jù)到數(shù)據(jù)庫 a 共享數(shù)據(jù)減1 a 插入數(shù)據(jù)到數(shù)據(jù)庫 b 執(zhí)行 b 插入數(shù)據(jù)到數(shù)據(jù)庫 b 共享數(shù)據(jù)減1 b 插入數(shù)據(jù)到數(shù)據(jù)庫 48:04
我們上面這段程序兩個線程全部執(zhí)行完所用的時間為 30 秒, 這里可以看出同步方法存在一個很大的弊端.
就是說我們的某個線程開始執(zhí)行方法時, 無論我們操作的是不是共享數(shù)據(jù), 別的線程都會等待此線程釋放鎖. 然后繼續(xù)執(zhí)行.
可是我們在插入數(shù)據(jù)到數(shù)據(jù)庫的時候, 并不是在操作共享數(shù)據(jù), 那么我們有沒有什么辦法, 只同步操作共享數(shù)據(jù)的那部分代碼呢?
我們就可以使用 synchronized 同步代碼塊, 將程序修改成下面樣子.
public class PrintObject { public void printString(){ try { System.out.println(Thread.currentThread().getName() + " 執(zhí)行"); System.out.println(Thread.currentThread().getName() + " 插入數(shù)據(jù)到數(shù)據(jù)庫"); // 讓線程休眠, 模擬出網(wǎng)絡(luò)延時 Thread.sleep(5000); synchronized(this) { System.out.println(Thread.currentThread().getName() + " 共享數(shù)據(jù)減1"); Thread.sleep(5000); } System.out.println(Thread.currentThread().getName() + " 插入數(shù)據(jù)到數(shù)據(jù)庫"); Thread.sleep(5000); if (Thread.currentThread().getName().equals("b")) { SimpleDateFormat df = new SimpleDateFormat("mm:ss"); System.out.println(df.format(new Date())); } } catch (InterruptedException e) { e.printStackTrace(); } } }
執(zhí)行結(jié)果
54:12 b 執(zhí)行 a 執(zhí)行 b 插入數(shù)據(jù)到數(shù)據(jù)庫 a 插入數(shù)據(jù)到數(shù)據(jù)庫 a 共享數(shù)據(jù)減1 a 插入數(shù)據(jù)到數(shù)據(jù)庫 b 共享數(shù)據(jù)減1 b 插入數(shù)據(jù)到數(shù)據(jù)庫 54:32
減少了10秒的執(zhí)行時間, 提高了執(zhí)行效率.
同步方法和同步代碼塊的鎖都是同一把鎖. 同步方法獲取的是該方法的對象鎖, 而同步代碼塊獲取中的參數(shù)是 this, 表示當(dāng)前對象. 所以獲取的是同一把鎖.靜態(tài)同步 synchronized 方法與 synchronized(class) 代碼塊
synchronized 關(guān)鍵字可以應(yīng)用在 static 靜態(tài)方法上, 表示當(dāng)前的 *.java 文件對應(yīng)的 Class 類進(jìn)行持鎖.
雖然運(yùn)行結(jié)果與 synchronized 關(guān)鍵字加到非 static 靜態(tài)方法上的結(jié)果類似, 但是是對 Class 類進(jìn)行加鎖, 而 Class 鎖可以對類的所有對象起作用.
synchronized (DemoApplication.class) { }
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/73654.html
摘要:上篇中初探了的一些功能和在前端自動化測試方面的可行性,本篇主要分析下的實現(xiàn)方式和源碼。文件分析完整文件目錄運(yùn)行生成目錄分析出了及其組件代碼,可用和值的分析的文件和下面的五個文件。相關(guān)文章前端自動化上篇初步調(diào)研前端自動化下篇實踐應(yīng)用 上篇中初探了page-monitor的一些功能和在前端自動化測試方面的可行性,本篇主要分析下page-monitor的實現(xiàn)方式和源碼。 mode-modul...
摘要:構(gòu)造函數(shù)默認(rèn)空閑的最大連接數(shù)為個,的時間為秒通過構(gòu)造函數(shù)可以看出默認(rèn)的空閑的最大連接數(shù)為個,的時間為秒。實例化實例化是在實例化時進(jìn)行的在的構(gòu)造函數(shù)中調(diào)用了省略省略緩存操作提供對進(jìn)行操作的方法分別為和幾個操作。 1.引子 在了解OkHttp的復(fù)用連接池之前,我們首先要了解幾個概念。 TCP三次握手 通常我們進(jìn)行HTTP連接網(wǎng)絡(luò)的時候我們會進(jìn)行TCP的三次握手,然后傳輸數(shù)據(jù),然后再釋放連接...
摘要:本篇博客主要針對虛擬機(jī)的晚期編譯優(yōu)化,內(nèi)存模型與線程,線程安全與鎖優(yōu)化進(jìn)行總結(jié),其余部分總結(jié)請點(diǎn)擊虛擬總結(jié)上篇,虛擬機(jī)總結(jié)中篇。 本篇博客主要針對Java虛擬機(jī)的晚期編譯優(yōu)化,Java內(nèi)存模型與線程,線程安全與鎖優(yōu)化進(jìn)行總結(jié),其余部分總結(jié)請點(diǎn)擊Java虛擬總結(jié)上篇 ,Java虛擬機(jī)總結(jié)中篇。 一.晚期運(yùn)行期優(yōu)化 即時編譯器JIT 即時編譯器JIT的作用就是熱點(diǎn)代碼轉(zhuǎn)換為平臺相關(guān)的機(jī)器碼...
摘要:安裝后已經(jīng)完成了安裝,并且等待其他的線程被關(guān)閉。激活后在這個狀態(tài)會處理事件回調(diào)提供了更新緩存策略的機(jī)會。并可以處理功能性的事件請求后臺同步推送。廢棄狀態(tài)這個狀態(tài)表示一個的生命周期結(jié)束。 showImg(https://segmentfault.com/img/bVbwWJu?w=2056&h=1536); 不知不覺,已經(jīng)來到了最后的下篇 其實我寫的東西你如果認(rèn)真去看,跟著去寫,應(yīng)該能有...
摘要:前端個靈魂拷問,徹底搞明白你就是中級前端工程師上篇感覺大家比較喜歡看這種類型的文章,以后會多一些。所有依賴這個模塊的語句,都定義在一個回調(diào)函數(shù)中,等到加載完成之后,這個回調(diào)函數(shù)才會運(yùn)行。此規(guī)范其實是在推廣過程中產(chǎn)生的。 showImg(https://segmentfault.com/img/bVbwAMU?w=700&h=394); 前端20個靈魂拷問,徹底搞明白你就是中級前端工程師...
閱讀 2814·2021-11-24 10:23
閱讀 1183·2021-11-17 09:33
閱讀 2539·2021-09-28 09:41
閱讀 1452·2021-09-22 15:55
閱讀 3669·2019-08-29 16:32
閱讀 1940·2019-08-29 16:25
閱讀 1079·2019-08-29 11:06
閱讀 3449·2019-08-29 10:55