在操做系統中,一個應用程序的執行實例就是進程,進程有獨立的內存空間和系統資源,在任務管理器中能夠看到進程。java
線程是CPU調度和分派的基本單位,也是進程中執行運算的最小單位,可完成一個獨立的順序控制流程,固然一個進程中能夠有多個線程。編程
多線程:一個進程中同時運行了多個線程,每一個線程用來完成不一樣的工做。多個線程交替佔用CPU資源,並不是真正的並行執行。多線程
使用多線程能充分利用CPU的資源,簡化編程模型,帶來良好的用戶體驗。ide
一個進程啓動後擁有一個主線程,主線程用於產生其餘子線程,並且主線程必須最後完成執行,它執行各類關閉動做。測試
在Java中main()方法爲主線程入口,下面使用 Thread 類查看主線程名。this
public class MainThread { public static void main(String[] args) { //獲取當前線程 Thread t=Thread.currentThread(); System.out.println("當前線程名字:"+t.getName()); //自定義線程名字 t.setName("MyThread"); System.out.println("當前線程名字:"+t.getName()); } }
在Java中建立線程有兩種方式
1.繼承 java.lang.Thread 類
2.實現 java.lang.Runnable 接口操作系統
(1)定義MyThread類繼承Thread類線程
(2)重寫run()方法,編寫線程執行體code
public class MyThread extends Thread{ //重寫run方法 @Override public void run() { for (int i = 1; i <= 10; i++) { System.out.println(Thread.currentThread().getName()+":"+i); } } }
(3)建立線程對象,調用start()方法啓動線程對象
public class TestMyThread { public static void main(String[] args) { MyThread myThread=new MyThread(); //啓動線程 myThread.start(); } }
多個線程同時啓動後是交替執行的,線程每次執行時長由分配的CPU時間片長度決定
修改 TestMyThread.java 觀察多線程交替執行
public class TestMyThread { public static void main(String[] args) { MyThread myThread1=new MyThread(); MyThread myThread2=new MyThread(); myThread1.start(); myThread2.start(); } }
多運行幾回觀察效果
啓動線程可否直接調用 run()方法?
不能,調用run()方法只會是主線程執行。調用start()方法後,子線程執行run()方法,主線程和子線程並行交替執行。
(1)定義MyRunnable類實現Runnable接口
(2)實現run()方法,編寫線程執行體
public class MyRunnable implements Runnable{ //實現 run方法 @Override public void run() { for (int i = 1; i <= 10; i++) { System.out.println(Thread.currentThread().getName()+":"+i); } } }
(3)建立線程對象,調用start()方法啓動線程
public class TestMyRunnable { public static void main(String[] args) { Runnable runnable=new MyRunnable(); //建立線程,傳入runnable Thread t=new Thread(runnable); t.start(); } }
建立狀態:線程建立完成,好比 MyThread thread=new MyThread
就緒狀態:線程對象調用 start() 方法,線程會等待CPU分配執行時間,並無立馬執行
運行狀態:線程分配到了執行時間,進入運行狀態。線程在運行中發生禮讓 (yield)
會回到就緒狀態
阻塞狀態:執行過程當中遇到IO操做或代碼 Thread.sleep()
,阻塞後的線程不能直接回到運行狀態,須要從新進入就緒狀態等待資源的分配。
死亡狀態:天然執行完畢或外部干涉終止線程
線程調度指按照特定機制爲多個線程分配CPU的使用權
線程調度經常使用方法
方法 | 說明 |
---|---|
setPriority(int newPriority) | 更改線程的優先級 |
static void sleep(long millis) | 在指定的毫秒數內讓當前正在執行的線程休眠 |
void join() | 等待該線程終止 |
static void yield() | 暫停當前正在執行的線程對象,並執行其餘線程 |
void interrupt() | 中斷線程 |
boolean isAlive() | 測試線程是否處於活動狀態 |
線程優先級由1~10表示,1最低,默認有限級爲5。優先級高的線程得到CPU資源的機率較大。
public class TestPriority { public static void main(String[] args) { Thread t1=new Thread(new MyRunnable(),"線程A"); Thread t2=new Thread(new MyRunnable(),"線程B"); //最大優先級 t1.setPriority(Thread.MAX_PRIORITY); //最小優先級 t2.setPriority(Thread.MIN_PRIORITY); t1.start(); t2.start(); } }
讓線程暫時睡眠指定時長,線程進入阻塞狀態,睡眠時間事後線程會再進入可運行狀態。
休眠時長以毫秒爲單位,調用sleep()方法須要處理 InterruptedException異常。
public class TestSleep { public static void main(String[] args) { for (int i = 1; i <= 10; i++) { System.out.println("第 "+i+" 秒"); try { //讓當前線程休眠1秒 Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
使用 join() 方法實現,能夠認爲是線程的插隊,會先強制執行插隊的線程。
public class JoinThread implements Runnable{ @Override public void run() { for (int i = 1; i <=10; i++) { System.out.println("線程名:"+Thread.currentThread().getName()+" i:"+i); } System.out.println("插隊線程執行完畢!"); } }
public class TestJoin { public static void main(String[] args) { Thread joinThread=new Thread(new JoinThread(),"插隊的線程"); //啓動後與主線程交替執行 joinThread.start(); for (int i = 1; i <= 10; i++) { if (i==5) { try { System.out.println("====開始插隊強制執行===="); joinThread.join(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("線程名:"+Thread.currentThread().getName()+" i:"+i); } System.out.println("主線程執行完畢!"); } }
最一開始執行,主線程 main 和 "插隊的線程"是交替執行。當主線程的循環到第5次的時候,調用 "插隊的線程"的join方法,開始強制執行"插隊的線程",待"插隊的線程"執行完後,才繼續恢復 main 線程的循環。
使用 yield() 方法實現,禮讓後會暫停當前線程,轉爲就緒狀態,其餘具備相同優先級的線程得到運行機會。
下面咱們實現Runnable接口,在run方法中實現禮讓,建立兩個線程,達到某種條件時禮讓。
public class YieldThread implements Runnable{ @Override public void run() { for (int i = 1; i <= 10; i++) { System.out.println("線程名:"+Thread.currentThread().getName()+" i:"+i); //當前線程執行到5後發生禮讓 if (i==5) { System.out.println(Thread.currentThread().getName()+" 禮讓:"); Thread.yield(); } } } }
public class TestYield { public static void main(String[] args) { Thread t1=new Thread(new YieldThread(),"A"); Thread t2=new Thread(new YieldThread(),"B"); t1.start(); t2.start(); } }
只是提供一種可能,不能保證必定會實現禮讓
首先看一個多線共享同一個資源引起的問題
倉庫有10個蘋果,小明、小紅、小亮每次能夠從倉庫中拿1個蘋果,拿完蘋果後倉庫中的蘋果數量-1。
先編寫倉庫資源類,實現接口
//這個實現類將被多個線程對象共享 public class ResourceThread implements Runnable{ private int num=10; @Override public void run() { while(true) { if (num<=0) { break; } num--; try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"拿走一個,還剩餘:"+num); } } }
編寫測試類,建立兩個線程對象,共享同一個資源
public class TestResource { public static void main(String[] args) { ResourceThread resource=new ResourceThread(); //使用同一個Runnable實現類對象 Thread t1=new Thread(resource,"小明"); Thread t2=new Thread(resource,"小紅"); Thread t3=new Thread(resource,"小亮"); t1.start(); t2.start(); t3.start(); } }
運行後咱們發現,每次拿完蘋果後的剩餘數量出現了問題,使用同步方法能夠解決這個問題。
語法:
訪問修飾符 synchronized 返回類型 方法名(參數列表){ …… }
synchronized 就是爲當前的線程聲明一個鎖
修改 ResourceThread.java 實現同步
//這個實現類將被多個線程對象共享 public class ResourceThread implements Runnable{ private int num=10; private boolean isHave=true; @Override public void run() { while(isHave) { take(); } } //同步方法 public synchronized void take() { if (num<=0) { isHave=false; return; } num--; try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"拿走一個,還剩餘:"+num); } }
實現同步的第二種方式同步代碼塊
語法:
synchronized(syncObject){ //須要同步的代碼 }
syncObject爲需同步的對象,一般爲this
修改 ResourceThread.java 改成同步代碼塊
//這個實現類將被多個線程對象共享 public class ResourceThread implements Runnable{ private int num=10; private boolean isHave=true; @Override public void run() { while(isHave) { synchronized(this) { if (num<=0) { isHave=false; return; } num--; try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"拿走一個,還剩餘:"+num); } } } }