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