多線程(multithreading),是指從軟件或者硬件上實現多個線程併發執行的技術。java
就是在單個程序中同時運行多個線程來完成不一樣的工做。面試
併發:指兩個或多個事件在同一個時間段內發生。
並行:指兩個或多個事件在同一時刻發生(同時發生)。
3.程序、進程與多線程
1)程序
Java源程序和字節碼文件被稱爲「程序」 (Program),是一個靜態的概念。安全
2) 進程
是指一個內存中運行的應用程序,每一個進程都有一個獨立的內存空間,一個應用程序能夠同時運行多個進程;進程也是程序的一次執行過程,是系統運行程序的基本單位;系統運行一個程序便是一個進程從建立、運行到消亡的過程。多線程
線程是進程中的一個執行單元,負責當前進程中程序的執行,一個進程中至少有一個線程。一個進程中是能夠有多個線程的,這個應用程序也能夠稱之爲多線程程序。併發
Java使用java.lang.Thread類表明線程,全部的線程對象都必須是Thread類或其子類的實例。ide
Java中經過繼承Thread類來建立並啓動多線程的步驟以下:測試
定義Thread類的子類,並重寫該類的run()方法,該run()方法的方法體就表明了線程須要完成的任務,所以把run()方法稱爲線程執行體
建立Thread子類的實例,即建立了線程對象
調用線程對象的start()方法來啓動該線程
自定義線程類:this
public class MyThread extends Thread { //定義指定線程名稱的構造方法 public MyThread(String name) { //調用父類的String參數的構造方法,指定線程的名稱 super(name); } /** * 重寫run方法,完成該線程執行的邏輯 */ @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(getName()+":正在執行!"+i); } } }
測試類:線程
public class Demo01 { public static void main(String[] args) { //建立自定義線程對象 MyThread mt = new MyThread("新的線程!"); //開啓新線程 mt.start(); //在主方法中執行for循環 for (int i = 0; i < 10; i++) { System.out.println("main線程!"+i); } } }
構造方法:3d
public Thread():分配一個新的線程對象。
public Thread(String name):分配一個指定名字的新的線程對象。
public Thread(Runnable target):分配一個帶有指定目標新的線程對象。
public Thread(Runnable target,String name):分配一個帶有指定目標新的線程對象並指定名字。
經常使用方法:
public String getName():獲取當前線程名稱。
public void start():致使此線程開始執行;Java虛擬機調用此線程的run方法。
public void run():此線程要執行的任務在此處定義代碼。
public static void sleep(long millis):使當前正在執行的線程以指定的毫秒數暫停(暫時中止執行)。
public static Thread currentThread():返回對當前正在執行的線程對象的引用
採用java.lang.Runnable也是很是常見的一種,咱們只須要重寫run方法便可。
步驟以下:
定義Runnable接口的實現類,並重寫該接口的run()方法,該run()方法的方法體一樣是該線程的線程執行體。
建立Runnable實現類的實例,並以此實例做爲Thread的target來建立Thread對象,該Thread對象纔是真正的線程對象。
調用線程對象的start()方法來啓動線程。
自定義線程類:
public class MyRunnable implements Runnable { @Override public void run(){ for(inti=0;i<20;i++){ System.out.println(Thread.currentThread().getName()+""+i); } } }
測試類:
public class Demo{ public static void main(String[]args){ //建立自定義類對象線程任務對象 MyRunnable mr = new MyRunnable(); //建立線程對象 Thread t = new Thread(mr,"小強"); t.start(); for(inti=0;i<20;i++){ System.out.println("旺財"+i); } } }
實現Runnable接口比繼承Thread類所具備的優點:
適合多個相同的程序代碼的線程去共享同一個資源。
能夠避免java中的單繼承的侷限性。
增長程序的健壯性,實現解耦操做,代碼能夠被多個線程共享,代碼和線程獨立。
線程池只能放入實現Runable或Callable類線程,不能直接放入繼承Thread的類。
/** * 用Lambda表達式來建立多線程 */ public class Thread004Lambda { public static void main(String[] args) { Thread t = new Thread(()->{ System.out.println("我是一個線程對象"); }); t.start();//啓動線程 System.out.println("我是主線程"); } }
1.線程的五種狀態
新生狀態: new
就緒狀態: runnable
運行狀態: running
阻塞狀態: blocked
執行完畢: dead
死亡狀態是線程生命週期中的最後一個階段。線程死亡的緣由有兩個。一個是正常運行的線程完成了它的所有工做;另外一個是線程被強制性地終止,如經過執行stop或destroy方法來終止一個線程。
不要調用 stop destory 方法 ,太暴力,一盆冷水讓其中止。
自定義類:
/** * * 終止線程的套路 * 一、加入標識 * 二、線程體的執行須要依賴標識 * 三、對外提供一個能夠修改標識的方法 * 四、外部適當的時機調用方法修改標識 */ class MyThread03 implements Runnable{ private boolean flag = true; @Override public void run() { int i = 0; while (true){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("在和小美聊天,在說第"+(i++)+"句話"); if(!flag){ break; } } } public void stopThread(){ flag = false; } }
測試類:
public class Thread005 { public static void main(String[] args) throws Exception{ MyThread03 mt = new MyThread03(); Thread t = new Thread(mt); t.start();//開啓線程 Thread.sleep(2000); mt.stopThread(); } }
sleep方法:Sleep時別的線程也不能夠訪問鎖定對象。
yield方法: 讓出CPU的使用權,從運行態直接進入就緒態。讓CPU從新挑選哪個線程進入運行狀態。
join方法: 等待當前線程死亡,當某個線程等待另外一個線程執行結束後,才繼續執行時,使用另外一個線程的join方法。使調用該方法的線程在此以前執行完畢,也就是等待調用該方法的線程執行完畢後再往下繼續執行
一個線程的默認優先級是5
Thread.MIN_PRIORITY = 1
Thread.MAX_PRIORITY = 10
Thread.NORM_PRIORITY = 5
注意:優先級低只是意味着得到調度的機率低。並非絕對先調用優先級高後調用優先級低的線程。
自定義類:
class FatherThread implements Runnable { @Override public void run() { System.out.println("爸爸想抽菸,發現煙抽完了"); System.out.println("爸爸讓兒子去買包紅塔山"); Thread son = new Thread(new SonThread()); son.start(); // 啓動新的線程 System.out.println("爸爸等兒子買菸回來"); try { son.join(); // 一直傻傻地等 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("能夠抽菸了"); } } class SonThread implements Runnable { public void run() { System.out.println("兒子出門去買菸"); System.out.println("兒子買菸須要10分鐘"); try { for (int i = 1; i <=10;i++) { System.out.println(Thread.currentThread().getName() + " 第" + i + "分鐘"); Thread.sleep(10); } } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " 兒子買菸回來了"); } }
測試類:
public class Demo008Thread { public static void main(String[] args)throws Exception { Thread t1 = new Thread(new SonThread(),"t1"); Thread t2 = new Thread(new SonThread(),"t2"); // int p = t1.getPriority();獲取優先級 t1.setPriority(10);//設置線程優先級 t2.setPriority(1); t1.start(); t2.start(); Thread.sleep(1000); //System.out.println(t.isAlive()); // 查看線程是否還活着 } }
當咱們使用多個線程訪問同一資源的時候,且多個線程中對資源有寫的操做,就容易出現線程安全問題。
要解決上述多線程併發訪問一個資源的安全性問題:也就是解決重複票與不存在票問題,Java中提供了同步機制(synchronized)來解決。
同步代碼塊:synchronized關鍵字能夠用於方法中的某個區塊中,表示只對這個區塊的資源實行互斥訪問。
格式:
synchronized(同步鎖){ 須要同步操做的代碼 }
同步鎖:
對象的同步鎖只是一個概念,能夠想象爲在對象上標記了一個鎖.
鎖對象能夠是任意類型。
多個線程對象要使用同一把鎖。
同步方法:使用synchronized修飾的方法,就叫作同步方法,保證A線程執行該方法的時候,其餘線程只能在方法外等着。
格式:
public synchronized void method(){ 可能會產生線程安全問題的代碼 } 1 2
同步鎖是誰?
對於非static方法,同步鎖就是this。
對於static方法,咱們使用當前方法所在類的字節碼對象(類名.class)。
綜合案例:
/** * 帳戶類 */ class Account { String aname; // 帳戶名 int money; // 帳戶餘額 public Account(String aname, int money) { this.aname = aname; this.money = money; } }
class Drawing implements Runnable{ Account account = null; int money = 0; // 準備取錢的金額 int acturalMoney = 0; // 實際取走的錢 public Drawing(Account account, int money) { this.account = account; this.money = money; } @Override public void run() { synchronized (account){//同步代碼塊 // 若是餘額不夠, 取不了錢 if (account.money < money) { System.out.println(" 餘額不足, 沒去走錢"); return; } try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } account.money -= money; // 取了錢,餘額變少 acturalMoney = money; // 取出來的錢 System.out.println(Thread.currentThread().getName() + "取了 " + acturalMoney + " 還剩 " + account.money); } } }
測試類:
/** * 兩我的操做同一個帳戶 */ public class Demo010Thread { public static void main(String[] args) throws Exception{ // 開戶 Account account = new Account("高富帥",100); System.out.println(account.money); Drawing d = new Drawing(account,90); // 你去取錢 Thread t = new Thread(d,"你" ); Thread t1 = new Thread(d,"女友" ); t.start(); t1.start(); Thread.sleep(1500); System.out.println(account.money); } }
###4.死鎖及解決方案
1)死鎖的概念
死鎖是指兩個或兩個以上的進程在執行過程當中,因爲競爭資源或者因爲彼此通訊而形成的一種阻塞的現象,若無外力做用,它們都將沒法推動下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱爲死鎖進程。
如何解決死鎖問題:
每每是程序邏輯的問題。須要修改程序邏輯。儘可能不要同時持有兩個對象鎖。