標籤:java多線程 進程調度 守護進程
在前一篇文章中說到,所有處在就緒狀態中的線程,作業系統會選擇優先順序最高的優先進行調度,那麼是不是優先順序高的線程就一定比優先順序低的線程先執行呢?線程的優先順序又是怎麼劃分的呢?這篇文章,樓樓就要來說說這個問題啦!歡迎關注我的個人部落客頁www.anycodex.com
1.線程的優先順序
在Java中,線程優先順序的範圍為0-10,整數值越大,說明優先順序更高。
MAX_PRIORITY 10,最高優先順序
MIN_PRIORITY 1,最低優先順序
NORM_PRIORITY 5,預設優先順序
setPriority(int x),設定線程的優先順序
getPriority(),擷取線程的優先順序
既然線程之間有優先順序,那是不是優先順序高的就一定比優先順序低的線程先執行呢?答案是不是的。只能說優先順序高的線程有更大的可能性獲得CPU,但這並不意味著就一定會先執行。
小夥伴們,我們下面來看一段代碼的例子吧,呼呼(~ o ~)~zZ
package net.mindview.util;//第一步、實現Runnable介面class MyThreadRunning implements Runnable{ //第二步、重寫run方法 public void run() { for (int i = 0; i <= 3; i++) { System.out.println(Thread.currentThread().getName()); } }}public class MyThread { public static void main(String[] args) { //第三步、執行個體化自訂的線程類對象 MyThreadRunning mtr1 = new MyThreadRunning(); MyThreadRunning mtr2 = new MyThreadRunning(); MyThreadRunning mtr3 = new MyThreadRunning(); //第四步、執行個體化一個Thread類對象並將自訂的線程類對象作為參數傳入,後面一個參數為線程名 Thread thread1 = new Thread(mtr1, "Thread 1 is running"); Thread thread2 = new Thread(mtr2, "Thread 2 is running"); Thread thread3 = new Thread(mtr3, "Thread 3 is running"); thread1.setPriority(Thread.MAX_PRIORITY); thread2.setPriority(Thread.MIN_PRIORITY); thread3.setPriority(Thread.NORM_PRIORITY); //第五步、調用start方法啟動線程 thread1.start(); thread2.start(); thread3.start(); }}運行結果:
Thread 1 is running
Thread 1 is running
Thread 2 is running
Thread 1 is running
Thread 3 is running
Thread 3 is running
Thread 3 is running
Thread 3 is running
Thread 1 is running
Thread 2 is running
Thread 2 is running
Thread 2 is running
噹噹當,小夥伴們是不是明白了呢?優先順序高的線程原來並不一定會先執行啊?只是有比較大的可能會獲得CPU調度。小夥伴們,可能會問,都設定優先順序了,還是不能保證線程按照我所希望的先後順序去執行,我還有什麼招呢?嗯,看完下面的內容,你就能學到一些招了,它就是我們的線程調度。
2.線程調度
關於線程調度,我們需要瞭解,只有良好的調度,才能發揮良好的系統效能,提高程式的執行效率。但是,再怎麼好的調度,也都無法精確的控製程序的運行。通常我們採用線程調度的方法主要有sleep,yield和join等方法。下面我們一個一個看哈!
sleep()方法,sleep就如英文意思所釋,睡眠。當某個程式進入睡眠狀態時,就會將CPU資源交給其他線程,而當休眠時間到時,程式又自動由阻塞狀態進入就緒狀態,等待CPU資源的調度。sleep()參數是毫秒級的,如sleep(1000)是讓線程休眠1s。調用這個方法的時候,需要捕獲InterruptedException異常。看下面的代碼吧
package net.mindview.util;//第一步、實現Runnable介面class MyThreadRunning implements Runnable{ //第二步、重寫run方法 public void run() { for (int i = 0; i <= 3; i++) { System.out.println(Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }}public class MyThread { public static void main(String[] args) { //第三步、執行個體化自訂的線程類對象 MyThreadRunning mtr1 = new MyThreadRunning(); MyThreadRunning mtr2 = new MyThreadRunning(); MyThreadRunning mtr3 = new MyThreadRunning(); //第四步、執行個體化一個Thread類對象並將自訂的線程類對象作為參數傳入,後面一個參數為線程名 Thread thread1 = new Thread(mtr1, "Thread 1 is running"); Thread thread2 = new Thread(mtr2, "Thread 2 is running"); Thread thread3 = new Thread(mtr3, "Thread 3 is running"); //第五步、調用start方法啟動線程 thread1.start(); thread2.start(); thread3.start(); }}運行結果:
Thread 2 is running
Thread 3 is running
Thread 1 is running
Thread 3 is running
Thread 2 is running
Thread 1 is running
Thread 2 is running
Thread 1 is running
Thread 3 is running
Thread 3 is running
Thread 2 is running
Thread 1 is running
從運行結果中可以看出,線程無法精確的控制先後順序。每次運行結果都有可能不一樣。
yield方法也像英文的意思一樣,就是讓步。暫停當前線程,讓出CPU已耗用時間,進入就緒狀態,從而讓其他線程獲得CPU時間。不過對於搶佔式作業系統沒有什麼意義,或者說沒有效果。
package net.mindview.util;//第一步、實現Runnable介面class MyThreadRunning1 implements Runnable{ //第二步、重寫run方法 public void run() { for (int i = 0; i <= 3; i++) { System.out.println(Thread.currentThread().getName()); } }}//第一步、實現Runnable介面class MyThreadRunning2 implements Runnable{ //第二步、重寫run方法 public void run() { for (int i = 0; i <= 3; i++) { System.out.println(Thread.currentThread().getName()); Thread.yield(); } }}public class MyThread { public static void main(String[] args) { //第三步、執行個體化自訂的線程類對象 MyThreadRunning1 mtr1 = new MyThreadRunning1(); MyThreadRunning2 mtr2 = new MyThreadRunning2(); //第四步、執行個體化一個Thread類對象並將自訂的線程類對象作為參數傳入,後面一個參數為線程名 Thread thread1 = new Thread(mtr1, "Thread 1 is running"); Thread thread2 = new Thread(mtr2, "Thread 2 is running"); //第五步、調用start方法啟動線程 thread1.start(); thread2.start(); }}運行效果: 如果不加yield的可能的運行結果:
Thread 1 is running Thread 1 is running
Thread 2 is running Thread 2 is running
Thread 1 is running Thread 1 is running
Thread 2 is running Thread 1 is running
Thread 1 is running Thread 1 is running
Thread 2 is running Thread 2 is running
Thread 1 is running Thread 2 is running
Thread 2 is running Thread 2 is running
從運行效果中可以看出,線程2每執行一次就會讓出CPU資源一次,但也只是可能的運行結果。
join方法的作用可能從英文意思上理解不是那麼簡單,那其實呢,join方法的作用是想讓一個線程等待另一個線程執行完畢後再繼續執行。可以添加參數,long類型的毫秒,等待該進程終止最長為多少秒。如thread.mt()就是等thread線程執行完之後,再執行其他的程式。我們可以看下面的代碼:
package net.mindview.util;//第一步、繼承Thread類class MyThreadRunning extends Thread{ //建構函式 public MyThreadRunning() { super("My Thread"); } //第二步、重寫run方法 public void run() { for (int i = 0; i <= 3; i++) { System.out.println("建立一個線程" + getName()+i); try { sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }}public class MyThread { public static void main(String[] args) { //第三步、執行個體化自訂的線程類對象 MyThreadRunning mtr = new MyThreadRunning(); //第四步、調用start方法從而啟動run方法 mtr.start(); try { mtr.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("mtr你執行完了是吧?我是主線程,我要來列印了。"); }}運行結果:
建立一個線程My Thread0
建立一個線程My Thread1
建立一個線程My Thread2
建立一個線程My Thread3
mtr你執行完了是吧?我是主線程,我要來列印了。
3.守護進程
守護進程也叫後台進程,是一種為其他線程提供服務的一種線程。當虛擬機器檢測到沒有使用者進程執行了的話,即使還有後台進程,JVM也有可能會退出的。
相關介面:
setDaemon(boolean)將一個線程設定為後台進程;
Thread.isDaemon()判斷一個線程是否為後台進程。
我們可以來看下面的代碼:
package net.mindview.util;//第一步、繼承Thread類public class MyThread { public static void main(String[] args) { Thread t1 = new MyCommon(); Thread t2 = new Thread(new MyDaemon()); t2.setDaemon(true); //設定為守護線程 t2.start(); t1.start(); } } class MyCommon extends Thread { public void run() { for (int i = 0; i < 5; i++) { System.out.println("線程1第" + i + "次執行!"); try { Thread.sleep(7); } catch (InterruptedException e) { e.printStackTrace(); } } } } class MyDaemon implements Runnable { public void run() { for (long i = 0; i < 9999999L; i++) { System.out.println("後台線程第" + i + "次執行!"); try { Thread.sleep(7); } catch (InterruptedException e) { e.printStackTrace(); } } } }運行結果:
線程1第0次執行!
後台線程第0次執行!
後台線程第1次執行!
線程1第1次執行!
線程1第2次執行!
後台線程第2次執行!
線程1第3次執行!
後台線程第3次執行!
線程1第4次執行!
後台線程第4次執行!
後台線程第5次執行!
同學們,由運行結果中是不是可以看出,後台線程還沒有執行完呢,程式就退出了。
以上就是我們關於Java多線程的第二部分內容了,總結一下,這篇文章主要講Java多線程的調度問題。調度方法主要有睡眠啊,讓步啊,以及join啊之類的。但是要清楚,無論程式員怎麼進行調度,也都無法實現線程的精確控制。當時良好的線程調度可以提高程式啟動並執行效率。最後我們講到守護線程,也就是後台線程,這裡需要注意的事,虛擬機器檢測到沒有使用者線程運行了的話,不管有沒有後台線程,都會退出。嗯,就這麼多了,小夥伴們,都掌握了嗎?下面一篇文章,我們要學習的是經典問題,生產者和消費者之類的線程同步問題,加油加油加油,未完待續哦~~~
【搞懂Java多線程之二】多線程調度及守護進程