Java十分鐘入門多線程中篇
舉例說明:
我們知道飛機在天上飛行是有固定的航線(可以理解成線程),每個機場都有最大的運行負載能力,當運行情況超過瞭負載能力的時候,這就需要塔臺調度參與,會根據每架飛機的優先級排序。當在航線的時候,如果出現緊急情況,會讓其他飛機避讓,讓這架飛機優先級提高,先降落。這就是調度,計算機程序線程運行也是這樣的。
1、線程的調度:
在Java多線程中,主要可以通過下面四個方法來分配CPU的使用權:
- 設置優先級(Priority) 設置線程的優先級,值是1-10
- 休眠(sleep) 單位毫秒,讓本線程屬於阻塞狀態,CPU會執行其他線程
- 強制運行(join) 讓這個線程強制獲取CPU資源來運行
- 禮讓(yield) 暫停正在執行的線程,讓其他線程先執行,執行完瞭在接著執行
1、設置優先級(Priority):
有兩個線程,分別設置最大優先級和最小優先級:
public class MyThread implements Runnable{ @Override public void run() { for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName()+"正在運行:"+i); } } public static void main(String[] args) { Thread t1 = new Thread(new MyThread(),"線程A:"); Thread t2 = new Thread(new MyThread(),"線程B***:"); //設置優先級: 最高為10 最低為1 t1.setPriority(Thread.MAX_PRIORITY); t2.setPriority(Thread.MIN_PRIORITY); //顯示線程優先級: System.out.println("線程A的優先級是:"+t1.getPriority()); System.out.println("線程B的優先級是:"+t2.getPriority()); t1.start(); t2.start(); } }
結果:
2、休眠(sleep)
Thread.sleep();——–單位是毫秒,讓本線程屬於阻塞狀態,CPU會執行其他線程:
public class ThreadSleep { public static void main(String[] args) { sleepTime(5); } private static void sleepTime(int time) { for (int i = 0; i < 5; i++) { System.out.println("主線程執行瞭:"+i+"s"); try{ //讓線程休眠,進入阻塞狀態 Thread.sleep(1000); //休眠時間為1000毫秒 } catch (InterruptedException e) { e.printStackTrace(); } } } }
結果:
3、強制運行(join)
顧名思義,就是讓某個線程強制進入執行:
子線程:
public class MyThread { }
測試類:
public class Test { public static void main(String[] args) throws InterruptedException { Thread t1= new Thread(new MyThread(),"我是子線程"); t1.start(); //當主線程執行任務1-10時,如果執行到5就讓子線程t1強制進來執行,直到執行完瞭才讓主線程繼續執行任務 for (int i = 0; i < 6; i++) { if (i==2){ t1.join(); } System.out.println(Thread.currentThread().getName()+"正在運行:"+i); } } }
結果:
4、禮讓(yield)
暫停正在執行的線程,讓其他線程先執行,執行完瞭在接著執行:
public class MyThread implements Runnable{ //線程禮讓,讓本線線程阻塞,其他線程先執行 //這裡就是A線程運行二次後,禮讓,讓B 線程先執行 //也是理論上的,就是不管怎麼樣第二次後面肯定讓B先執行,但是後面就隨機瞭 @Override public void run() { for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName()+"正在運行:"+i); if(i == 2){ Thread.yield(); } } } }
測試類:
public class Test { public static void main(String[] args) { Thread t1 = new Thread(new MyThread(),"線程A:"); Thread t2 = new Thread(new MyThread(),"----線程B"); t1.start(); t2.start(); } }
結果:
2、定時器線程:
定時器就是可以設置某個時間點來執行某個事件,比如系統每周刪除依次日志文件,或者在指定的日期關閉系統,定時器本身就是一個線程任務來在指定的時候執行該任務。定時器是繼承TimerTask來重寫run方法,專門處理定時任務。
演示Demo:
public class MyThread extends TimerTask { @Override public void run() { //把任務定義在run方法中 showMyself(); } public void showMyself(){ System.out.println("被Run方法執行的"); } }
測試類:
public class Test { public static void main(String[] args) { Timer timer = new Timer(); //設置5秒後執行這個任務,並且每1秒重復執行這個任務 timer.schedule(new MyThread(),5000,1000); } }
結果:
3、線程的同步:
首先我們先看一個demo:
創建瞭兩個線程對象,一個線程A任務用於執行print1,另一個線程B任務用於執行print2:
public void print1(){ System.out.print("中"); System.out.println("國"); } public void print2(){ System.out.print("浙"); System.out.println("江"); } }
測試類:
public class Test { public static void main(String[] args) { Printer p = new Printer(); //A: new Thread(new Runnable() { @Override public void run() { while(true){ p.print1(); } } },"線程A:").start(); //B: new Thread("線程B:"){ @Override public void run(){ while (true){ p.print2(); } } }.start(); } }
這個程序就是當線程A執行的時候,輸出中國,當B執行的時候,輸出浙江,理論上是沒有任何問題,但是我們看一下結果:
我們發現出問題瞭,其實這就是非線程同步(異步):
- 同步:提交請求->等待服務器處理->處理完返回 這個期間客戶端瀏覽器不能幹任何事
- 異步:請求通過事件觸發->服務器處理(這是瀏覽器仍然可以作其他事情)->處理完畢
其實非線程同步在有些系統是很危險的問題,比如12306,如果使用非線程同步,那麼後果可想而知,那麼該如何同步呢? 這裡介紹一個常用的方法,就是上鎖:
如果兩端代碼(兩個線程任務)是同步的,那麼CPU同一時間隻能執行一個任務,相當於給該線程上瞭一把鎖, 在該線程沒有執行完這段代碼或者任務的時候,其他線程是不能占用CPU資源執行任務的。直到執行完瞭該線 程的代碼,其他線程才可以執行。
更好的理解(舉例):
你去公共廁所上廁所(大的),當你進去後,需要把門關上並鎖著,這就是上鎖,為瞭保證你正常的結束(保證線程正常運行完),這期間其他人是不能進來的。
Synchronized 鎖:
兩個線程任務使用同一個對象為鎖,那麼兩個線程方法是同步的 我們把上面的方法上個鎖:
class Demo { } public class Printer { //創建任意一個對象,隻要是對象相同就是鎖相同,就是同步的! Demo d = new Demo(); public void print1(){ //當進入print1這個方法時,synchronized就給這個方法上瞭一個鎖 synchronized (d){ System.out.print("中"); System.out.println("國"); } } public void print2(){ //當進入print2這個方法時,synchronized也給這個方法上瞭一個鎖 synchronized (d){ System.out.print("浙"); System.out.println("江"); } } }
這樣輸出後就不會出現上面的那個問題,這裡就不發結果截圖啦。大傢可以自己試一試,看看是否解決瞭這個問題~
我們還可以把鎖直接定義在方法上,比如這樣子:
public static synchronized void print1(){ System.out.print("中"); System.out.println("國"); }
如果是靜態方法,上鎖的方式是通過.class字節碼對象:
public static void print1() { synchronized (Printer.class) { System.out.print("中"); System.out.println("國"); } }
小結: 這篇文章主要介紹瞭線程的調度(四種)、線程的定時以及線程的同步,內容也不少,多線程是Java的一個難點,也沒用一些華麗的圖片來演示,在學習過程中會比較枯燥,包括我自己也是。大傢也可以看看視頻,加深理解和印象!
到此這篇關於Java十分鐘入門多線程中篇的文章就介紹到這瞭,更多相關Java 多線程內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- Java多線程Thread類的使用及註意事項
- 新手瞭解java 多線程基礎知識
- 新手初學Java網絡編程
- 一篇文章掌握Java Thread的類及其常見方法
- 淺談Java鎖的膨脹過程以及一致性哈希對鎖膨脹的影響