日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > java >内容正文

java

Java高并发编程:定时器、互斥、同步通信技术

發(fā)布時(shí)間:2025/4/16 java 16 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java高并发编程:定时器、互斥、同步通信技术 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

筆記摘要

這里分析了多線程的一些細(xì)節(jié)問(wèn)題,并介紹了傳統(tǒng)定時(shí)器的創(chuàng)建,同時(shí)實(shí)現(xiàn)了根據(jù)自己的調(diào)度計(jì)劃的自定義定時(shí)器,對(duì)于傳統(tǒng)互斥技術(shù)中發(fā)現(xiàn)的內(nèi)部類問(wèn)題,進(jìn)行了分析,最后對(duì)于同步通信技術(shù),是重點(diǎn),分析了如何處理類似的問(wèn)題,如何設(shè)計(jì)能夠更加清晰簡(jiǎn)單,體現(xiàn)了高內(nèi)聚和程序的健壯性

1. 多線程的幾個(gè)知識(shí)點(diǎn)

1.1 為何使用實(shí)現(xiàn)Runnable的方式創(chuàng)建線程更普遍?

new Runnable()的方式,更加體現(xiàn)面向?qū)ο蟮乃枷?/p>

通過(guò) new Thread()創(chuàng)建一個(gè)線程,代碼封裝在runnable對(duì)象中,代碼和線程獨(dú)立分開(kāi)來(lái),但最終將它們組合在一起。

Thread thread = new Thread(new Runnable() {@Overridepublic void run() {// code} });

1.2 獲取線程名的時(shí)候,應(yīng)使用currentThread().getName()方式

因?yàn)閠his.getName()方式,只有在當(dāng)前對(duì)象是Thread的時(shí)候可以,當(dāng)我們使用runnable方式時(shí),this代表的是runnable對(duì)象,它僅是要運(yùn)行代碼的宿主,而不是線程,當(dāng)然編譯也無(wú)法通過(guò)(沒(méi)有此方法)。

1.3 創(chuàng)建線程的兩種傳統(tǒng)方式的run方法執(zhí)行問(wèn)題

查看Thread類的run()方法的源代碼,可以看到其實(shí)這兩種方式都是在調(diào)用Thread對(duì)象的run()方法,如果Thread類的run()方法沒(méi)有被覆蓋,并且為該Thread對(duì)象設(shè)置了一個(gè)Runnable對(duì)象,該run方法會(huì)調(diào)用Runnable對(duì)象的run()方法。

下面是Thread類的run()方法的源碼,可以看到runnable對(duì)象也是調(diào)用了Thread的run()方法。

當(dāng)runnable對(duì)象不為null,并且有自己的run()方法,則執(zhí)行自己的,如果target為null,則Thread類的run()方法什么也不執(zhí)行,所以我們?cè)趧?chuàng)建線程的時(shí)候不直接創(chuàng)建Thread對(duì)象,而是創(chuàng)建其子類對(duì)象,目的是為了復(fù)寫run方法,把要執(zhí)行的代碼放進(jìn)去,否則該線程沒(méi)有意義

Private Runnable target; public void run() { if (target != null) { target.run(); } }

1.4 多線程的運(yùn)行

問(wèn)題1:如果在Thread子類覆蓋的run()方法中編寫了運(yùn)行代碼,也為Thread子類對(duì)象傳遞了一個(gè)Runnable對(duì)象,那么,線程運(yùn)行時(shí)的執(zhí)行代碼?

是子類的run方法的代碼?還是Runnable對(duì)象的run()方法的代碼?如:

// 匿名內(nèi)部類的方式實(shí)現(xiàn)的一個(gè)子類,并在構(gòu)造方法中傳入了一個(gè)Runnable對(duì)象 new Thread(new Runnable() {public void run() {// code} }) {@Overridepublic void run() {super.run();// code} }.start();

會(huì)運(yùn)行子類的run方法。因?yàn)楫?dāng)某個(gè)對(duì)象調(diào)用start方法之后,就會(huì)去找自己對(duì)象的run方法,如果沒(méi)有就會(huì)找父類的run方法,父類的run方法會(huì)找runnable運(yùn)行。

其實(shí)就是多態(tài)的一種體現(xiàn),覆蓋了父類的就執(zhí)行自己的,沒(méi)有覆蓋就去找父類的執(zhí)行

問(wèn)題2:多線程機(jī)制是否會(huì)提高程序的運(yùn)行效率?

多線程機(jī)制并不會(huì)提高程序的運(yùn)行效率,反而性能更低,因?yàn)镃PU需要在不同線程之間頻繁切換。

1.5 多線程下載的誤解?

多線程下載其實(shí)是搶了服務(wù)器的帶寬,一個(gè)線程代表一個(gè)用戶,每個(gè)線程分配的帶寬是相等的,開(kāi)啟的線程多,就會(huì)分配更多的帶寬,是在搶資源,而不是自己更快。

2. 傳統(tǒng)定時(shí)器:Timer類

定時(shí)器有兩種:一種在指定時(shí)間只執(zhí)行一次,另一種先在指定時(shí)間執(zhí)行一次,之后每隔指定時(shí)間循環(huán)執(zhí)行。

該示例說(shuō)明了定時(shí)器的創(chuàng)建方式,并通過(guò)自定義定時(shí)器的方式,在一個(gè)定時(shí)器內(nèi)部通過(guò)不同切換秒數(shù),來(lái)實(shí)現(xiàn)在不同的間隔時(shí)間實(shí)現(xiàn)循環(huán)爆炸,另外還通過(guò)兩個(gè)類之間的互相實(shí)現(xiàn)相同的效果

import java.util.Date; import java.util.Timer; import java.util.TimerTask; public class TraditionalTimerTest { private static int count = 0; public static void main(String[] args) { //創(chuàng)建一個(gè)定時(shí)器并調(diào)度任務(wù) /* new Timer().schedule(new TimerTask() { @Override public void run() { System.out.println("bombing!"); } }, 3000);//3秒以后爆炸 */ //}, 10000,3000); //10秒以后爆炸,以后每隔3秒炸一次 //自定義一個(gè)定時(shí)器 class MyTimerTask extends TimerTask{ @Override public void run() { count = (count+1)%2; //在0和1之間切換 System.out.println("bombing!"); new Timer().schedule(/*new TimerTask() { @Override public void run() { System.out.println("bombing!"); } }*/new MyTimerTask(),2000+2000*count); //實(shí)現(xiàn)循環(huán),不能用this,因?yàn)槭悄涿?#xff0c;所以只能執(zhí)行一次, //就像炸彈一樣,炸完后就沒(méi)有了,必須布置新的炸彈 //所以創(chuàng)建一個(gè)類,每次在最后new一個(gè)新的炸彈 } } //開(kāi)啟定時(shí)器,每隔2秒調(diào)用一次MyTimerTask new Timer().schedule(new MyTimerTask(), 2000); //為了觀察定時(shí)器任務(wù)的執(zhí)行:每隔1秒打印一次當(dāng)前秒數(shù) while(true){ System.out.println(new Date().getSeconds()); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }

使用互相調(diào)用的方式實(shí)現(xiàn)間隔2秒和4秒的連環(huán)爆炸

import java.util.Date; import java.util.Timer; import java.util.TimerTask; public class MyTraditionalTimer { public static void main(String[] args){ new Timer().schedule(new MyTimerTask(), 4000); //打印當(dāng)前秒數(shù) while(true){ System.out.println(new Date().getSeconds()); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } //每隔2秒調(diào)用MyTimerTask2 class MyTimerTask extends TimerTask{ @Override public void run() { // TODO Auto-generated method stub System.out.println("boomping!!!"); new Timer().schedule(new MyTimerTask2(),2000); } } //每隔4秒調(diào)用MyTimerTask2 class MyTimerTask2 extends TimerTask{ @Override public void run() { // TODO Auto-generated method stub System.out.println("boomping!!!"); new Timer().schedule(new MyTimerTask(), 4000); } }

3. 調(diào)度框架:quarts

Quartz是一個(gè)開(kāi)源的作業(yè)調(diào)度框架,它完全由Java寫成,并設(shè)計(jì)用于J2SE和J2EE應(yīng)用中。它提供了巨大的靈活性而不犧牲簡(jiǎn)單性。你能夠用它來(lái)為執(zhí)行一個(gè)作業(yè)而創(chuàng)建簡(jiǎn)單的或復(fù)雜的調(diào)度。它有很多特征,如:數(shù)據(jù)庫(kù)支持,集群,插件,EJB作業(yè)預(yù)構(gòu)建,JavaMail及其它,支持cron-like表達(dá)式等等。

對(duì)于定時(shí)器中不能很好實(shí)現(xiàn)的需求,我們可以想到quarts,這里并沒(méi)有介紹其使用方式,以后開(kāi)發(fā)用到,能夠記起,去查資料

4. 傳統(tǒng)線程互斥技術(shù)

發(fā)現(xiàn)的問(wèn)題:在主函數(shù)內(nèi)部不能創(chuàng)建內(nèi)部類的實(shí)例對(duì)象

內(nèi)部類的一個(gè)重要特點(diǎn)就是可以訪問(wèn)外部類的成員變量,成員變量是對(duì)象身上的,對(duì)象創(chuàng)建完后,成員變量才分配空間,所以內(nèi)部類訪問(wèn)外部類的成員變量需要外部類的實(shí)例對(duì)象。而靜態(tài)方法先存在,所以不可以。

解決方式:

可以將內(nèi)部類定義為靜態(tài)的,或者將創(chuàng)建內(nèi)部類的實(shí)例對(duì)象的語(yǔ)句封裝在一個(gè)外部類的成員方法中,這里定義了一個(gè)init方法,因?yàn)榉椒ㄕ{(diào)用需要對(duì)象,這個(gè)對(duì)象就是將來(lái)調(diào)用該方法的對(duì)象

示例說(shuō)明:

本示例主要是對(duì)上面的問(wèn)題進(jìn)行了展示,另外對(duì)過(guò)去的互斥技術(shù)中的鎖所使用的對(duì)象進(jìn)行了分析

public class TraditionalThreadSychronized { public static void main(String[] args) { //無(wú)法創(chuàng)建,必須關(guān)聯(lián)一個(gè)外部類的實(shí)例對(duì)象,可以定義一個(gè)方法, //或者將外部類定義為靜態(tài) //final Outputer outputer = new Outputer(); //編譯錯(cuò)誤 new TraditionalThreadSychronized().init(); } //方法需要對(duì)象調(diào)用,所以就關(guān)聯(lián)了一個(gè)外部類的對(duì)象 private void init() { final Outputer outputer = new Outputer(); new Thread(new Runnable() { @Override public void run() { while (true) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } outputer.output("11111"); } } }).start(); new Thread(new Runnable() { @Override public void run() { while (true) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } // outputer.output("are you happy?"); outputer.output2("22222"); } } }).start(); } static class Outputer { // public void output(String name) { // String lock = ""; int len = name.length(); // synchronized(lock){ 使用同一把鎖,任意對(duì)象都可以 // synchronized(this){ 同步函數(shù)使用的是鎖是this,即outputer對(duì)象 synchronized (Outputer.class) { // 靜態(tài)方法的鎖只能是class字節(jié)碼對(duì)象 for (int i = 0; i < len; i++) { System.out.print(name.charAt(i)); } System.out.println(); } } // 同步函數(shù)使用的是鎖是this /* 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(); } } */ //定義同步的靜態(tài)方法 public static 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(); } } }

5. 傳統(tǒng)線程同步通信技術(shù)

這里通過(guò)一道面試題進(jìn)行講解

需求:子線程循環(huán)10次,接著主線程循環(huán)100次,接著又回到子線程循環(huán)10次,接著再回到主線程又循環(huán)100次,如此循環(huán)50次

1、思路:

使用面向?qū)ο蟮姆绞剿伎?#xff0c;子線程的任務(wù)是循環(huán)10次,子線程的任務(wù)是循環(huán)100次,所以可以將它們各自的任務(wù)封裝起來(lái),在封裝內(nèi)部實(shí)現(xiàn)各自的同步(鎖是放在代表要操作的資源的類的內(nèi)部方法中),最后別的對(duì)象來(lái)調(diào)用,循環(huán)50次即可

2、Eclipse小技巧:

這里打印結(jié)果過(guò)長(zhǎng),我們可以使用eclipse將打印結(jié)果輸出到文件中:
Run As → Run Configurations→ Common → File前打勾 → 指定路徑

3、鎖對(duì)象的定義

兩個(gè)線程執(zhí)行的代碼片段要實(shí)現(xiàn)同步互斥的效果,它們必須用同一個(gè)鎖對(duì)象,鎖是放在代表要操作的資源的類的內(nèi)部方法中,而不是在線程代碼中。

4、實(shí)現(xiàn)按指定的順序執(zhí)行

需要用到wait,Notify,當(dāng)輪到自己要執(zhí)行的時(shí)候,讓對(duì)象去喚醒自己,可以定義一個(gè)標(biāo)識(shí),來(lái)決定誰(shuí)可以執(zhí)行

5、wait方法必須放在synchronized的里面,而且調(diào)用它的對(duì)象必須和synchronized的對(duì)象是同一個(gè)。

6、While比if更嚴(yán)謹(jǐn),因?yàn)闀?huì)循環(huán)判斷執(zhí)行條件,所以可以防止偽喚醒,(并不是期望的對(duì)象來(lái)喚醒自己)。

經(jīng)驗(yàn):要用到共同數(shù)據(jù)(包括同步鎖)或共同算法的若干個(gè)方法應(yīng)該歸于同一個(gè)類上,在這個(gè)類的內(nèi)部去管理各個(gè)方法的狀態(tài),這種設(shè)計(jì)正好體現(xiàn)了高類聚和程序的健壯性

public class TraditionalThreadCommunication { public static void main(String[] args){ //獲取一個(gè)業(yè)務(wù)對(duì)象 final Business business = new Business(); //子線程 new Thread(new Runnable(){ @Override public void run() { for(int i=1;i<=50;i++){ /*for(int j=0;j<10;j++){ System.out.println("sub thread sequence of"+i+",loop of "+j); }*/ business.sub(i); } } }).start(); //主線程 for(int i=1;i<=50;i++){ /* for(int j=0;j<100;j++){ System.out.println("main thread sequence of"+i+", loop of "+j); } */ business.main(i); } } } //定義一個(gè)業(yè)務(wù)類 class Business{ //定義一個(gè)boolean型變量來(lái)決定子線程和主線程的執(zhí)行權(quán) private boolean bShouldSub = true; //子線程 public synchronized void sub(int i){//把同步的鎖放在資源身上 //不該子線程執(zhí)行,等待 if(!bShouldSub){ try { this.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } for(int j=1;j<=10;j++){ System.out.println("sub thread sequence of"+i+",loop of "+j); } bShouldSub = false; this.notify(); //喚醒主線程 } //主線程 public synchronized void main(int i){ //若是子線程執(zhí)行,主線程等待 if(bShouldSub){ try { this.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } for(int j=1;j<=100;j++){ System.out.println("main thread sequence of"+i+", loop of "+j); } bShouldSub = true; this.notify(); //喚醒子線程 } }

總結(jié)

以上是生活随笔為你收集整理的Java高并发编程:定时器、互斥、同步通信技术的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。