程序就是一堆靜態的代碼,存儲在硬盤上。程序若是不運行,本質就是一個文件。java
程序一次運行產生進程,進程一直向前運行,直到進程結束。安全
單任務操做系統:一段時間只能運行一個程序(任務)。CPU利用率很是低。多線程
引入進程的概念併發
把程序的一次運行產生進程(內存空間、資源、程序的執行堆棧)ide
進程做爲操做系統分配資源的基本單位。this
多任務操做系統spa
一臺電腦就一個CPU,多個任務輪流使用CPU,從宏觀上看,一段時間有多個任務正在運行。操作系統
從微觀上看,一個時間點只有一個任務在運行。.net
CPU時間片線程
多個進程經過CPU時間片輪轉實現多任務(進程)。把這種現象叫作併發操做
並行:
一個時間段,多個任務同時進行,多個CPU運行各自的進行。
線程的引入
解決實時性問題
綜合性案例
Thread 類位於java.lang中,表示進程中的執行線程。實現多線程有兩種方式
繼承Thread和實現Runnable接口實現多線程的優缺點
[1] 繼承Thread的線程類不能再繼承其餘類,實現Runnable接口的類還能夠繼承其餘類。
[2] 實現Runnable接口的線程類,可讓多個線程共享線程實現類的資源。
新生狀態 用new關鍵字創建一個線程後,該線程對象就處於新生狀態。 處於新生狀態的線程有本身的內存空間,經過調用start()方法進入就緒狀態。
就緒狀態 處於就緒狀態線程具有了運行條件,但還沒分配到CPU,處於線程就緒隊列,等待系統爲其分配CPU。 當系統選定一個等待執行的線程後,它就會從就緒狀態進入執行狀態,該動做稱爲「CPU調度」。 運行狀態 在運行狀態的線程執行本身的run方法中代碼,直到等待某資源而阻塞或完成任何而死亡。 若是在給定的時間片內沒有執行結束,就會被系統給換下來回到等待執行狀態。 阻塞狀態 處於運行狀態的線程在某些狀況下,如執行了sleep(睡眠)方法,或等待I/O設備等資源,將讓出CPU並暫時中止本身運行,進入阻塞狀態。 在阻塞狀態的線程不能進入就緒隊列。只有當引發阻塞的緣由消除時,如睡眠時間已到,或等待的I/O設備空閒下來,線程便轉入就緒狀態,從新到就緒隊列中排隊等待,被系統選中後從原來中止的位置開始繼續執行。 死亡狀態 死亡狀態是線程生命週期中的最後一個階段。線程死亡的緣由有三個,一個是正常運行 的線程完成了它的所有工做;另外一個是線程被強制性地終止,如經過stop方法來終止一個 線程【不推薦使用】;三是線程拋出未捕獲的異常。
|
public static void main(String[] args) {
System.out.println(Thread.MIN_PRIORITY); System.out.println(Thread.MAX_PRIORITY); System.out.println(Thread.NORM_PRIORITY);
//主線程的優先級(默認優先級) System.out.println(Thread.currentThread().getPriority());
Thread01 t1 = new Thread01(); // 設置線程的優先級 t1.setPriority(Thread.MAX_PRIORITY); t1.start();
Thread01 t2 = new Thread01(); // 設置線程的優先級 t2.setPriority(Thread.MIN_PRIORITY); t2.start();
} |
線程優先級高,被cpu調度的機率大,不表示必定先運行。
判斷線程是否處於活動狀態。
Thread01 t1 = new Thread01(); System.out.println(t1.isAlive()); // 設置線程的優先級 t1.setPriority(Thread.MAX_PRIORITY); t1.start(); System.out.println(t1.isAlive()); |
線程調用start以後就處於活動狀態。
調用該方法的線程強制執行,其它線程處於阻塞狀態,該線程執行完畢後,其它線程再執行
join稱爲線程的強制執行,有可能被外界中斷產生InterruptedException 中斷異常。
public class Test02 { public static void main(String[] args){
Thread02 t = new Thread02("線程A"); t.start();
for (int i = 0; i < 5; i++) {
if(i == 2) { try { t.join(); } catch (InterruptedException e) { e.printStackTrace(); } }
System.out.println(Thread.currentThread().getName() + "->" + i); } } } |
在指定的毫秒數內讓當前正在執行的線程休眠(暫停執行)。休眠的線程進入阻塞狀態。
public static void main(String[] args) {
Thread03 t = new Thread03("線程A"); t.start();
Thread mainThread = Thread.currentThread(); System.out.println(mainThread.getName()+"即將進入休眠"); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); }
// 中斷線程 t.interrupt();
System.out.println(mainThread.getName()+"休眠完成"); } |
public static void main(String[] args) {
Thread mainThread = Thread.currentThread();
Thread04 t = new Thread04("線程A"); t.start();
for (int i = 0; i < 5; i++) { if (i == 2) { // yield 使當前禮讓一次 Thread.yield(); } System.out.println(mainThread.getName() + "->" + i); } } |
A hint to the scheduler that the current thread is willing to yield its current use of a processor. The scheduler is free to ignore this hint.
當前線程給cpu調度器一個暗示,暗示其想禮讓一次其擁有的cpu,CPU調度者也能夠狐狸此次暗示。此時當前線程進入就緒狀態。
[6] 線程的終止。
目前而言,不推薦使用stop直接終止線程。用interrupt()方法去中斷正在執行的線程,而在線程內部必定要寫捕獲中斷的異常。經過異常處理機制正常結束線程。
線程在執行過程當中,經過cpu的調度,執行軌跡不肯定,對共享資源的訪問很容易形成數據的錯誤。咱們稱這個錯亂稱爲線程安全問題。
原子性操做:一個操做要麼一次性作完,要麼根本不開始,不存在中間狀態。
案例:ATM取現操做
同步就是讓操做保持原子性!java提供兩種方式實現同步。
把全部的同步操做放到同步代碼塊中,
synchronized (mutex) { // .. . } |
mutex 稱爲互斥鎖/同步鎖。對共享資源進行加鎖實現同步。通常用共享資源做爲同步鎖,也稱同步監視器。
public class MyRun implements Runnable {
// 共享資源 private int count = 5;
public void run() { // 模擬一個窗口5我的 for (int i = 0; i < 5; i++) { // 同步代碼塊 // mutex 互斥鎖 synchronized (this) { if (count > 0) {
try { Thread.sleep(3000); count--; } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(Thread.currentThread().getName()+"賣出一張票,還剩" + count + "張票"); } } } } } |
總結
synchronized(obj){}中的obj稱爲同步監視器
同步代碼塊中同步監視器能夠是任何對象,可是推薦使用共享資源做爲同步監視器
若是同步代碼(原子性)不少,能夠考慮使用同步方法。
把普通方法用 synchronized 修飾,同步方法的同步監視器是this。
public class MyRun implements Runnable {
// 共享資源 private int count = 5;
public void run() { // 模擬一個窗口5我的 for (int i = 0; i < 5; i++) {
this.saleTicket();
} }
// 同步方法默認對this加鎖 private synchronized void saleTicket() { if (count > 0) {
try { Thread.sleep(3000); count--; } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(Thread.currentThread().getName()+"賣出一張票,還剩" + count + "張票"); } } |
線程t1,擁有A資源,再次申請B資源,線程t2,擁有B資源,再申請A資源,t1由於沒有申請到B資源而進入阻塞;t2由於沒有申請到A資源進入阻塞。此時兩個線程都處於阻塞狀態而不能正常結束,而此時cpu空轉,這種狀況稱爲死鎖。