深度分析:面試90%被問到的多線程、建立線程、線程狀態、線程安全,一次性幫你全搞定!

1、多線程

1.概述

多線程(multithreading),是指從軟件或者硬件上實現多個線程併發執行的技術。java

就是在單個程序中同時運行多個線程來完成不一樣的工做。面試

2.併發與並行

併發:指兩個或多個事件在同一個時間段內發生。
並行:指兩個或多個事件在同一時刻發生(同時發生)。
3.程序、進程與多線程
1)程序
Java源程序和字節碼文件被稱爲「程序」 (Program),是一個靜態的概念。安全

2) 進程
是指一個內存中運行的應用程序,每一個進程都有一個獨立的內存空間,一個應用程序能夠同時運行多個進程;進程也是程序的一次執行過程,是系統運行程序的基本單位;系統運行一個程序便是一個進程從建立、運行到消亡的過程。多線程

3)線程

線程是進程中的一個執行單元,負責當前進程中程序的執行,一個進程中至少有一個線程。一個進程中是能夠有多個線程的,這個應用程序也能夠稱之爲多線程程序。併發

2、建立線程類

1.繼承Thread類

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);
        }
    }
}

1)Thread類介紹

構造方法: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():返回對當前正在執行的線程對象的引用

2.實現Runnable接口

採用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);
        }
    }
}

3.Thread和Runnable的區別

實現Runnable接口比繼承Thread類所具備的優點:

適合多個相同的程序代碼的線程去共享同一個資源。

能夠避免java中的單繼承的侷限性。

增長程序的健壯性,實現解耦操做,代碼能夠被多個線程共享,代碼和線程獨立。

線程池只能放入實現Runable或Callable類線程,不能直接放入繼承Thread的類。

4.用Lambda表達式建立多線程

/**
 * 用Lambda表達式來建立多線程
 */
public class Thread004Lambda {
    public static void main(String[] args) {
        Thread t = new Thread(()->{
            System.out.println("我是一個線程對象");
        });

        t.start();//啓動線程
        System.out.println("我是主線程");
    }
}

3、線程狀態

1.線程的五種狀態
新生狀態: new
就緒狀態: runnable
運行狀態: running
阻塞狀態: blocked
執行完畢: dead
深度分析:面試90%被問到的多線程、建立線程、線程狀態、線程安全,一次性幫你全搞定!

死亡狀態是線程生命週期中的最後一個階段。線程死亡的緣由有兩個。一個是正常運行的線程完成了它的所有工做;另外一個是線程被強制性地終止,如經過執行stop或destroy方法來終止一個線程。

2.終止線程

不要調用 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();
    }
}

3.阻塞狀態(sleep/yield/join方法)

sleep方法:Sleep時別的線程也不能夠訪問鎖定對象。
yield方法: 讓出CPU的使用權,從運行態直接進入就緒態。讓CPU從新挑選哪個線程進入運行狀態。
join方法: 等待當前線程死亡,當某個線程等待另外一個線程執行結束後,才繼續執行時,使用另外一個線程的join方法。使調用該方法的線程在此以前執行完畢,也就是等待調用該方法的線程執行完畢後再往下繼續執行

4.線程基本信息

深度分析:面試90%被問到的多線程、建立線程、線程狀態、線程安全,一次性幫你全搞定!

一個線程的默認優先級是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());  // 查看線程是否還活着
    }
}

4、線程安全

1.線程同步

當咱們使用多個線程訪問同一資源的時候,且多個線程中對資源有寫的操做,就容易出現線程安全問題。

要解決上述多線程併發訪問一個資源的安全性問題:也就是解決重複票與不存在票問題,Java中提供了同步機制(synchronized)來解決。

2.同步代碼塊

同步代碼塊:synchronized關鍵字能夠用於方法中的某個區塊中,表示只對這個區塊的資源實行互斥訪問。
格式:

synchronized(同步鎖){
    須要同步操做的代碼
}

同步鎖:

對象的同步鎖只是一個概念,能夠想象爲在對象上標記了一個鎖.

鎖對象能夠是任意類型。
多個線程對象要使用同一把鎖。

3.同步方法

同步方法:使用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)死鎖的概念
死鎖是指兩個或兩個以上的進程在執行過程當中,因爲競爭資源或者因爲彼此通訊而形成的一種阻塞的現象,若無外力做用,它們都將沒法推動下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱爲死鎖進程。

如何解決死鎖問題:

每每是程序邏輯的問題。須要修改程序邏輯。儘可能不要同時持有兩個對象鎖。

相關文章
相關標籤/搜索