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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java里的线程控制

發布時間:2025/3/20 java 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java里的线程控制 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

這篇文章接著上篇文章<<java 線程簡介>> 寫的.

http://blog.csdn.net/nvd11/article/details/19118683


上一篇文章提到,? java程序猿可以利用類Thread或其接口Runnable開啟一條新線程.

但開啟一條新線程之后, 不能任由它不管啊.

其實java有很多方法讓程序猿控制線程的執行過程.



一, 線程的三種狀態切換

一個線程由線程類Thread或其派生類的start()啟動.

啟動后的線程具有3種狀態.


1.1 就緒狀態

注意, 1個線程t被t.start()函數啟動后, 并不是立即可以被cpu執行. 而是進入了就緒狀態.

在就緒狀態中的線程代表了有了cpu執行的資格. 由于想搶占cpu被執行的線程有很多. cpu并不一定立即執行t對應的線程.


實際上, 一般情況下在1個時間點, cpu只會執行1個線程, 但是這個cpu會不斷跳到另1個線程去執行它. 前提是這個"另1個進程是"就緒狀態.


1.2 運行狀態

相對地, cpu當前執行進程所在的狀態就是執行狀態.

一般來講, 一個程序運行中同1個時間點只會有1條線程處于運行狀態.????

但是cpu會不斷地切換所執行的線程.? 一旦cpu切換到另1個線程執行. 原來運行的線程就會從運行狀態切換到就緒狀態.?

這種行為我們就稱為cpu的調度.


1.3 阻塞狀態.

一旦1個線程遇到阻塞事件(例如sleep()函數), (無論它原來是在運行狀態還是就緒狀態),就會進入阻塞狀態.

處于阻塞狀態的線程無法被cpu調度, 也就是無法被cpu執行(進入運行狀態).

直接阻塞接觸后, 該線程返回就緒狀態.



簡單圖示如下:


1.4 現實例子


假如喲有A, B, C 三個人都想使用另1個人D都電腦上網,? 但是D只有1臺電腦。

然后D就讓A B C 三人首先坐在廳里都長沙發上, 然后D挑選1個人進房間使用電腦。

每隔一段時間D會把使用電腦都人趕回廳里都沙發上, 然后再從沙發上挑1個人進去使用電腦。


那么3個人就都有機會使用電腦, 令網絡上覺得A B C3個人有電腦都假象。


1.那么在房間使用電腦都人就相對于多線程的運行狀態。

2.坐在沙發的人就相當于多線程的就緒狀態, 他們都有機會被D選中使用電腦.

3.如過A突然肚子痛(阻塞事件)離開了沙發, 那么A就相當于進入了線程的阻塞狀態, 除非A再次返回沙發上, 才會有資格被D選中。



二, 線程的優先級設置

一般來講, 假如有兩條線程再執行, cpu是會隨機切換執行的。

但是實際上線程也有重要性的區別,我們可以利用優先級別設置來控制某1個線程更有機會搶占cpu資源。


Java提供1個線程調度器來監控程序中啟動后進入就緒狀態都所有線程。

線程調度器挑選執行線程的機制受到線程優先級的影響。


2.1 類Thread 的3個靜態常量成員

線程的優先級用數字表示, 范圍從1到10, 1個線程都默認優先級是5。

這個數字越大表示線程都優先級越高


類Thread還提供了3個靜態常量成員, 提供給程序猿,可以代替數字來使用。

他們分別是


Thread.MIN_PRIORITY=1

Thread.MAX_PRIORITY=10

Thread.NORM_PRIORITY=5


注意, 這個3個成員都是static final的. 代表都是它們都屬于類本身,而且不能被賦值。


2.2 線程對象獲取和設置優先級的方法。

2.2.1 int getPriority();

調用這個方法可以獲得對應線程對象都優先級。 例如 t.getPriority() 返回都就是線程對象t都當前優先級。


2.2.2 void setPriority(int newPriority)

線程對象可以調用這個方法來改變本身都優先級.

注意,參數范圍必須再1-10, 否則會拋出java.lang.IllegalArgumentException 異常。

2.3 設置優先級都一個例子

package Thread_kng.Td_priority_kng;class M_thrd_6 implements Runnable{public void run(){int i;Thread cur_thd = Thread.currentThread();for (i=1; i<101; i++){System.out.printf("Thread %s: priority is %d, i is %d\n", cur_thd.getName(),cur_thd.getPriority(),i);}} } public class Td_priority_1{public static void f(){M_thrd_6 s = new M_thrd_6();Thread t1 = new Thread(s);Thread t2 = new Thread(s);t1.setName("T1");t2.setName("T2");t1.start();t1.setPriority(Thread.MIN_PRIORITY); //set the priority to 1t2.setPriority(Thread.NORM_PRIORITY + 3); //set the priority to 8t2.start();} }

看上面的例子:

我利用實驗Runnable 接口的1個對象構造兩個子線程。

在run方法里。

這兩個子線程(T1, T2)循環100次把i輸出到屏幕上, 并順便輸出線程的名字,和線程都當前優先級(getPriority())都輸出到屏幕。


看例子下面的f()函數, 首先執行的是t1. 再執行的是t2.

但是t1的優先級別調低了, t2的優先級別調高了。


結果就是T2比T1先執行完成。

執行結果:



2.4 優先級對線程調度的真正影響

見到結果中,雖然T2的優先級比T1高得多, 但是T2和T1實際上還是不斷被切換執行的。


2.4.1 時間片論算法。

所謂時間片論算法就是指java中1個線程處于執行狀態的單次時間是一定的。

所以即使T2的優先級比T1高, 并不是指T2處于單次執行狀態的時間比較長。


假如單次執行狀態的時間是x毫秒,


那么無論T2還是T1,被執行x毫秒后,都會被強制退回就緒狀態, cpu再從就緒狀態的線程選1個使其進入執行狀態。

而優先級別高的線程被選中的機率相對更高


那么單位時間內, T2進入執行狀態的次數會比T1高。?


實際上, 這個單次執行時間比輸出100次i的時間小得多, 所以上面運行例子會見到T1 和 T2不斷切換執行。



2.4.2 實際開發中,java并不單純依賴優先級別來決定線程執行次序。

也就是講, 優先級別只是影響cpu調度的其中1個因數。


還有如下其他因數:

1. 突發事件, 例如打印機的打印線程發現打印機的紙張用完, 那么系統就會馬上執行打印線程的警告機制。

2. 線程執行時間, 通常會把消耗資源小,執行快的線程放在前面運行。

3. 最長等待時間, 一個線程即使優先級別很少, 但是超過了一段等待時間后, 會被強制進入執行狀態。


2.4 現實例子

舉回上面A B C三個人上網的時間, 優先級別就是A B C三個人跟D的個人關系了。

高優先級別的人每次再就緒狀態中被D選中的機率更大, 但是每次進入房間上網的時間還是一樣的。





三, 線程控制的常用方法

下面開始介紹線程的常用方法, 也是面試中問得比較多的地方:


3.1 sleep()

sleep(int n) 是1個常用的線程函數,參數s代表的是毫秒數字,他的作用是令線程停止執行并進入阻塞狀態n毫秒。

在這n毫秒內, 這個線程不能被cpu執行。

過了n毫秒后, 這個線程重新進入就緒狀態,但是不代表這個線程能馬上被cpu執行。

上面說過了, 就緒狀態的線程想要執行還需要取決于cpu的調度。


它在線程基類Thread中定義如下:


public static void sleep(long millis,
???????????????????????? int nanos)
????????????????? throws InterruptedException


??? 在指定的毫秒數加指定的納秒數內讓當前正在執行的線程休眠(暫停執行),此操作受到系統計時器和調度程序精度和準確性的影響。該線程不丟失任何監視器的所屬權。

??? 參數:
??????? millis - 以毫秒為單位的休眠時間。
??????? nanos - 要休眠的另外 0-999999 納秒。
??? 拋出:
??????? IllegalArgumentException - 如果 millis 值為負或 nanos 值不在 0-999999 范圍內。
??????? InterruptedException - 如果任何線程中斷了當前線程。當拋出該異常時,當前線程的中斷狀態 被清除



需要注意的是: sleep函數會拋出異常, 而且這個異常是非RuntimeException, 所以必須進行捕捉。

關于異常的文章里講過, 捕捉要不進行try catch, 要不在函數定義中使用throws.

但是sleep()函數最終都是卸載線程類的run()方法里面的。 而基類Thread的run()方法是不拋出任何異常的。 所以由于多態的存在,重寫的run()的方法不能throws任何異常.

所以一般在使用sleep函數時,必須使用try catch


下面是1個例子:

package Thread_kng.Td_ctrl;class M_thrd_7 implements Runnable{public void run(){java.text.DateFormat d1 = java.text.DateFormat.getDateTimeInstance();java.util.Date now;Thread cur_thd = Thread.currentThread();int i;for (i=0; i<10; i++){now = new java.util.Date();System.out.printf("Thread %s: i is %d, time is %s\n",cur_thd.getName(), i, d1.format(now));try{cur_thd.sleep(3000); //sleep 3 seconds, must be put into the try{}}catch(Exception e){}}} }public class Td_ctrl_1{public static void f(){M_thrd_7 s = new M_thrd_7();Thread t1 = new Thread(s);t1.setName("T1");t1.start();Thread t2 = new Thread(s);t2.setName("T2");t2.start();} }
這個例子的線程業務很簡單, 就是把i從9輸出到0, 但是每輸出1次暫停3000毫秒。

需要注意的是,sleep()函數是類Thread的一個成員方法, 所以使用sleep()方法的前提是1個Thread類或其派生類的一個對象。

在下面的f()方法中,利用1個對象創建了兩個線程t1.t2并啟動。

結果就是t1 和 t2兩條線程都是每3秒在屏幕輸出一次信息:


輸出結果:

Thread T1: i is 0, time is Feb 17, 2014 2:42:10 PM Thread T2: i is 0, time is Feb 17, 2014 2:42:10 PM Thread T1: i is 1, time is Feb 17, 2014 2:42:13 PM Thread T2: i is 1, time is Feb 17, 2014 2:42:13 PM Thread T1: i is 2, time is Feb 17, 2014 2:42:16 PM Thread T2: i is 2, time is Feb 17, 2014 2:42:16 PM Thread T1: i is 3, time is Feb 17, 2014 2:42:19 PM Thread T2: i is 3, time is Feb 17, 2014 2:42:19 PM Thread T1: i is 4, time is Feb 17, 2014 2:42:22 PM Thread T2: i is 4, time is Feb 17, 2014 2:42:22 PM Thread T1: i is 5, time is Feb 17, 2014 2:42:25 PM Thread T2: i is 5, time is Feb 17, 2014 2:42:25 PM Thread T1: i is 6, time is Feb 17, 2014 2:42:28 PM Thread T2: i is 6, time is Feb 17, 2014 2:42:28 PM Thread T1: i is 7, time is Feb 17, 2014 2:42:31 PM Thread T2: i is 7, time is Feb 17, 2014 2:42:31 PM Thread T1: i is 8, time is Feb 17, 2014 2:42:34 PM Thread T2: i is 8, time is Feb 17, 2014 2:42:34 PM Thread T1: i is 9, time is Feb 17, 2014 2:42:37 PM Thread T2: i is 9, time is Feb 17, 2014 2:42:37 PM gateman@TPEOS classes $

sleep() 是1個靜態方法, 一般來講直接調用Thread.sleep(x),? 就可以令當前執行的線程暫停x毫秒. 并不需要獲得當前的線程對象.

3.2 yield()

yield方法是基類Thread的另1個成員方法。

在中文JDK的解析是這樣的:

public static void yield()
暫停當前正在執行的線程對象,并執行其他線程。


但是這個解釋并不準確。

我們一般把yield方法簡稱為線程讓步。


準確的解析如下:

當t線程執行t.yield() 后,會馬上停止t的執行狀態, 并且令t退回到就緒狀態。


1. yield方法沒有參數。

2. yield 會令線程的執行狀態終止。

3. yield 會令線程退回到就緒狀態。

4. 然后cpu還重新調度, 選擇一個線程進入執行狀態,? 注意這個線程有可能是剛才yield退回就緒狀態的線程。

5. 所以java jdk api 中午的解析是不準確的。并不是暫停線程, 并執行其他線程, 而是暫停線程, 重新讓cpu調度。


也就是說1個線程執行yield后被退回就緒狀態, 如果它的優先級別高, 它是有可能馬上被cpu重新執行進行執行狀態的。


那么yield()的意義是什么呢, 本屌也不是很sure,? 但是如果1個線程,不斷循環執行1個包含yield的方法, 那么每一次循環它都會讓步一次。

間接地令到這個線程比其他不含yield的線程更低。


下面是1個例子:

package Thread_kng.Td_ctrl;class M_thrd_8 extends Thread{public M_thrd_8(String name){super(name);}public void run(){int i;for (i=0; i<1000; i++){System.out.printf("Thread %s: i is %d",this.getName(), i);this.yield();}} }class M_thrd_9 extends Thread{public M_thrd_9(String name){super(name);}public void run(){int i;for (i=0; i<1000; i++){System.out.printf("\nThread %s: i is %d\n",this.getName(), i);//this.yield();}} }public class Td_yield_1{public static void f(){M_thrd_8 t1 = new M_thrd_8("T1");t1.start();M_thrd_9 t2 = new M_thrd_9("T2");t2.start();} }
上面例子定義兩個基本相同的線程類, 都是循環把i 從0 輸出到 999。

但是t1 線程每1個循環都yield讓步一次, t2 線程沒有。

執行時, t2會比t1得到更多cpu資源。

如果這個兩個線程屬于不同進程, 那么具有yield方法的進程cpu占用率會稍低。


對于這個例子來說, 結果就是t2執行得比t1快, 而且比單純地設置優先級別明顯得多.

執行結果:

Thread T2: i is 956Thread T2: i is 957Thread T2: i is 958Thread T2: i is 959Thread T2: i is 960Thread T2: i is 961Thread T2: i is 962 Thread T1: i is 349 Thread T2: i is 963Thread T2: i is 964Thread T2: i is 965 Thread T1: i is 350 Thread T2: i is 966Thread T2: i is 967Thread T2: i is 968 Thread T1: i is 351 Thread T2: i is 969Thread T2: i is 970 Thread T1: i is 352 Thread T2: i is 971

3.3 sleep() 和 yield()的區別

我們還是舉回上面A B C三人用D的電腦上網的例子:


sleep()函數必須有個時間參數, 加入線程A執行了sleep(n),就相當于A有事去廁所, 時間是n。 這段時間內A處于阻塞狀態, 無法被D選中去上網的。

過了n時間后, A回來到沙發上進入就緒狀態, 但是還是需要D的調度才能去上網。


而yield方法就相當于正在上網的A被D拉回到沙發上, 然后重新等待D的調度, 但是有可能D還是選中A的, 也就是說A被拉回到沙發上, 但是馬上又可以去上網。



3.4 join()

join() 是基類Thread的另1個成員方法。


JDK API 是如此定義的:

public final void join()
??????????????? throws InterruptedException

等待該線程終止

詳細的定義是:

當線程t 調用t.join() 時, 暫停當前線程的執行, 除非t執行完成了, 當前線程繼續執行。

注意當前線程是執行t.join()的線程。


現實例子:

又是A B C三人上網的例子, 假如B線程執行A.join() , 也相當于B告訴D,我離開一會, 等A上完網時我就才回來上網。

注意,C不受影響哦, 實際上就是B 1個人暫時退出, A和C兩人簡單切換上網, 直至A上完網, B才回來就緒狀態!



值得注意的是, join()類似sleep()方法, 都會拋出異常。


下面是1個join()方法的例子:


package Thread_kng.Td_ctrl;class M_thrd_10 extends Thread{public M_thrd_10(String name){super(name);}也就是說t3必須等t1完成才能執行public void run(){int i;for (i=0; i<1000; i++){System.out.printf("Thread %s: i is %d\n",this.getName(), i);}} }class M_thrd_11 extends Thread{private M_thrd_10 t_join;public M_thrd_11(String name, M_thrd_10 t_join){super(name);this.t_join = t_join;}public void run(){int i;for (i=0; i<501; i++){System.out.printf("Thread %s: i is %d\n",this.getName(), i);}try{t_join.join(); }catch(Exception e){}for (; i<1000; i++){System.out.printf("Thread %s: i is %d\n",this.getName(), i);}} }public class Td_join_1{public static void f(){M_thrd_10 t1 = new M_thrd_10("T1"); M_thrd_10 t2 = new M_thrd_10("T2"); //will not impacted by t1.join()M_thrd_11 t3 = new M_thrd_11("T3",t1);t1.start();t3.start();t2.start();} }
上面定義了兩個線程類, 實例化了3個對象t1, t2, t3, 而t3的run()方法里, 調用了t1.join()。

也就是說t1. t2都把i從0輸出到999

但是t3先輸出到500 就必須等t1完成, 才繼續輸出501 到 999

也就是說t3必須等t1完成才能執行

但是t2是不受影響的。



執行結果:

Thread T1: i is 981 Thread T1: i is 982 Thread T1: i is 983 Thread T1: i is 984 Thread T1: i is 985 Thread T1: i is 986 Thread T1: i is 987 Thread T1: i is 988 Thread T1: i is 989 Thread T1: i is 990 Thread T1: i is 991 Thread T1: i is 992 Thread T1: i is 993 Thread T1: i is 994 Thread T1: i is 995 Thread T1: i is 996 Thread T1: i is 997 Thread T1: i is 998 Thread T1: i is 999 Thread T2: i is 899 Thread T3: i is 501 Thread T3: i is 502 Thread T3: i is 503 Thread T3: i is 504 Thread T3: i is 505 Thread T3: i is 506 Thread T3: i is 507 Thread T3: i is 508 Thread T3: i is 509 Thread T3: i is 510 Thread T3: i is 511 Thread T3: i is 512

3.5 wait() notify() notifyAll()

這個3個方法涉及同步的問題,? 我會在以后介紹java同步的博文里再講






總結

以上是生活随笔為你收集整理的Java里的线程控制的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。