面試官:小夥子你連多線程輸出數列都不會,還敢說本身會多線程?

1、前言

計算機的操做系統大多采用任務和分時設計,多任務是指在一個操做系統中能夠同時運行多個程序,例如,能夠在使用qq聊天的同時聽音樂,即有多個獨立運行的任務,每一個任務對應一個進程,每一個進程又能夠產生多個線程。java

1.進程

進程是程序的一次動態執行過程,它對應了從代碼加載、執行至執行完畢的一個完整過程,這個過程也是進程自己從產生、發展至消亡的過程。操做系統同時管理一個計算機系統中的多個進程,讓計算機系統中的多個進程輪流使用CPU資源。
進程的特色:多線程

  1. 進程是系統運行程序的基本單位
  2. 每個進程都有本身獨立的一塊內存空間、一組系統資源
  3. 每個進程的內部數據和狀態都是徹底獨立的

2.線程

線程是進程中執行運算的最小單位,一個進程在其執行過程當中能夠產生多個線程而線程必須在某個進程內執行。
線程是進程內部的一個執行單元,是可完成一個獨立任務的順序控制流程,若是在一個進程中同時運行了多個線程,用來完成不一樣的工做,則稱之爲多線程。
線程按處理級別能夠分爲核心級線程和用戶及線程併發

1.核心機線程
核心級線程是和系統任務相關的額線程,他負責處理不一樣進程之間的多個線程。容許不一樣進程中的線程按照同一相對優先調度方法對線程進行調度,使他們有條不紊的工做,能夠發揮多處理器的併發優點,以充分利用計算機的軟/硬件資源測試

2.用戶級線程
在開發程序時,因爲程序的須要而編寫的線程即用戶級線程,這些線程的建立,執行和消亡都是在編寫應用時進行控制的。對於用戶級線程的切換,一般發生在一個應用程序的諸多線程之間,如迅雷中的多線程下載就屬於用戶線程操作系統

3.線程和進程的聯繫以及區別
1.一個進程中至少有一個線程
2.資源分配給進程,同一個進程的全部線程共享該進程的全部資源
3.處理機分配給線程,即真正在處理機上運行的是線程線程

4.多線程的優點
1.多線程程序能夠帶來更好的用戶體驗,避免因程序執行過慢而致使出現計算機死機或者白屏的狀況。
2.多線程能夠最大限度地提升計算機系統的利用效率,如迅雷的多線程下載。設計

2、編寫線程類

每一個程序至少自動擁有一個線程,稱爲主線程。當程序加載到內存時啓動主線程。java程序中的main方法時主線程的入口,運行java程序時,會先執行這個方法。開發中,用戶編寫的線程通常都是指除了主線程以外的其餘線程
使用一個線程的過程通常有如下4個步驟code

  1. 定義一個線程,同時指明這個線程所要執行的代碼,即指望完成的功能
  2. 建立線程對象
  3. 啓動線程
  4. 終止線程

定義一個線程一般由兩種方法,分別是繼承java.lang.Thread類和java.lang.Runnable接口對象

1.使用Thread類建立線程

Thread類的經常使用方法:繼承

方法 說明
void run() 執行任務操做的方法
void start() 使該線程開始執行
void sleep(long mils) 在指定的毫秒數內讓當前正在執行的線程休眠(暫停執行)
String getName() 返回該線程的名稱
int getPririt() 返回線程的優先級
void setPriority(int newPoriority) 更改線程的優先級
Thread.State getState() 返回該線程的狀態
boolean is Alive() 測試線成是否處於活動狀態
void join() 等待該線程終止
void interrupt() 中斷線程
void yield() 暫停當前正在執行的線程對象,並執行其餘線程

建立線程時繼承Thread類並重寫Thread類run()方法。其中run方法時線程要執行操做任務的方法,因此線程要執行的操做代碼都要寫在run方法中,並經過調用start方法來啓動線程。
示例:使用繼承Thread類的方式來建立線程,在線程中輸入1-100的整數
實現步驟:
1.定義一個類來繼承Thread類,重寫run方法,在run方法中實現數據輸出
2.建立線程對象
3.調用start方法啓動線程

public class MyThread extends Thread{
    //示例:使用繼承Thread類的方式來建立線程,在線程中輸入1-100的整數
    private int count = 0;
    //重寫run方法
    public void run(){
        for (int i = 1; i <=100 ; i++) {
            System.out.println(i);
        }
    }
}

啓動線程

package xc.test1;

public class Test {
    public static void main(String[] args) {
        //實例化線程對象
        MyThread mt = new MyThread();
        //啓動線程
        mt.start();
    }
}

2.使用Runnable接口建立線程

雖然Thread類的方式建立線程簡單明瞭符合你們的習慣,但他也有一個缺點,若是定義的類已經繼承了其餘類則沒法再繼承Thread類。使用Runnable接口建立線程的方式能夠解決上述問題。
Runnable接口中聲明瞭一個run方法(),即public void run()。一個類能夠經過實現Runnable接口並實現其run()方法完成線程的全部活動,已實現的run方法稱爲該對象的線程體。任何實現runable接口的對象均可以做爲一個線程的目標對象。
示例:使用繼承Thread類的方式來建立線程,在線程中輸入1-100的整數
實現步驟:
1.定義了MyThread類實現Runable接口,並實現Runnable接口的run方法,在run方法中輸出數據
2.建立線程對象
3.調用start方法啓動線程

public class MyThread implements Runnable{
    //示例:使用繼承Thread類的方式來建立線程,在線程中輸入1-100的整數
    private int count = 0;
    //重寫run方法
    public void run(){
        for (int i = 1; i <=100 ; i++) {
            System.out.println(i);
        }
    }
}

啓動start

package xc.test1;

public class Test {
    public static void main(String[] args) {
        //實例化線程對象
       Thread thread = new Thread(new MyThread());
        //啓動線程
        thread.start();
    }
}

3、線程的狀態

線程生命週期4階段:新生狀態,可運行狀態,阻塞狀態,死亡狀態

1.新生狀態

線程在還沒有調用start方法以前就有了生命,線程僅僅是一個空對象,系統沒有爲其分配資源,此時只能啓動和終止線程,任何其餘操做都會發生異常。

2.可運行狀態

當調用start方法後啓動線程後,系統爲該線程分配出CPU外的所需資源,這時線程就處於可運行的狀態。
固然在這個狀態中,線程也可能未運行。對於只有一個CPU的機器而言,任什麼時候刻只能有一個處於可運行狀態的線程佔用處理機

3.阻塞狀態

一個正在運行的線程因某種緣由不能繼續運行時,進入阻塞狀態,阻塞狀態是不可運行的狀態。
致使阻塞狀態的緣由

  1. 調用了Thread的靜態方法sleep()
  2. 一個線程執行到一個I/O操做時,I/O操做還沒有完成
  3. 若是一個線程的執行須要獲得一個對象鎖,而這個對象的鎖正在被別的線程用,那麼會致使阻塞
  4. 線程的suspend()方法被調用而使線程被掛起時,線程進入阻塞狀態

3.死亡狀態

當一個線程的run方法運行完畢,stop方法被調用或者在運行過程當中出現未捕獲的異常時,線程進入死亡狀態。

4、線程調度

當同時有多個線程處於可運行狀態,他們須要排隊等待CPU資源,每一個線程會自動 得到一個線程的優先級,優先級的高低反映出線程的重要或緊急程度,可運行狀態的線程按優先級排隊。線程調度依據創建在優先級基礎上的「先到先服務」原則。
線程調度室搶佔式調度,即在當前線程執行過程當中若是有一個更高優先級的線程進入可運行狀態,則這個更高優先級的線程當即被調度執行。

1.線程優先級

線程的優先級用1~10表示,10表示優先級最高,默認值是5,每一個優先級對應一個Thread類的公用靜態常量
例如:public static final int NORM_PRIORITY=5;
每一個線程的優先級都介於Thread.MIN_PRIORITY和Thread.MAX_PRIORITY之間
線程的優先級能夠經過setPrioity(int grade)方法更改

2.實現線程調度的方法

實現線程調度的方法
1.join()方法
join方法使當前線程暫停執行,等待調用該方法的線程結束後再繼續執行本線程。
它有3中重載形式
public final void join()
public final void join(long mills)
public final void join(long mills,int nanos)

示例:使用join()阻塞線程
實現步驟:
1.定義線程類,輸出5次當前線程的名稱
2.定義測試類,使用join方法阻塞主線程

package xc.test2;

public class MyThread extends Thread {
    public MyThread(String name){
        super(name);
    }
    public void run(){
        for (int i = 0; i <5 ; i++) {
            //輸出當前線程的名稱
            System.out.println(Thread.currentThread().getName()+""+i);
        }
    }
}
package xc.test2;

public class Test {
    public static void main(String[] args) {
        //主線程運行五次後,開始運行MyThread線程
        for (int i = 0; i <10 ; i++) {
            if (i==5){
                MyThread t = new MyThread("MyThread");
                try {
                t.start();
                t.join();//把該線程經過join方法插入到主線程面前
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName()+""+i);
        }
    }
}

示例:使用sleep()方法阻塞線程
實現步驟:
定義線程
在run()方法中使用sleep()方法阻塞線程
定義測試類

package xc.test3;

public class Wait {
    //==示例:使用sleep()方法阻塞線程==
    public static void bySec(long s){
        for (int i = 0; i < s; i++) {
            System.out.println((i+1)+"秒");
        }
        try {
            Thread.sleep(1000);//括號中的是毫秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
package xc.test3;

public class Test {
    public static void main(String[] args) {
        System.out.println("wait");//提示等待
        Wait.bySec(5);//讓主線程等待五秒再執行
        System.out.println("start");//提示恢復執行

    }
}

2.yield()方法
語法格式:

  • public static void yield()

yield方法可以讓當前線程暫停執行,容許其餘線程執行,但該線程仍處於可運行狀態,並不變爲阻塞狀態。此時,系統選擇其餘相同或更高優先級線程執行,若無其餘相同或更高優先級線程,則該線程繼續執行。

示例:使用yield方法暫停線程
實現步驟:
1.定義兩個線程
2.在run方法中使用yield方法暫停線程
3.定義測試類

package xc.test4;

public class FirstThread  extends Thread{
    public void run(){
        for (int i = 0; i < 5; i++) {
            System.out.println("第一個線程的第"+(i+1)+"次運行");
            Thread.yield();//暫停線程
        }
    }
}
package xc.test4;

public class SecThread extends Thread{
    public void run(){
        for (int i = 0; i < 5; i++) {
            System.out.println("第二個線程的第"+(i+1)+"次運行");
            Thread.yield();
        }
    }
}
package xc.test4;

public class Test {
    public static void main(String[] args) {
        FirstThread f = new FirstThread();
        SecThread s = new SecThread();
        f.start();
        s.start();
    }
}

sleep方法與yield方法的區別

sleep()方法 yield()方法
使當前線程進入被阻塞的狀態 使當前線程進入暫停執行的狀態
即便沒有其餘等待運行的線程,當前線程也會等待指定的時間 若是沒有其餘等待執行的線程,當前線程會立刻恢復執行
其餘等待執行的線程的機會是均等的 會運行優先級相同或更高的線程

最後

感謝你看到這裏,看完有什麼的不懂的能夠在評論區問我,以爲文章對你有幫助的話記得給我點個贊,天天都會分享java相關技術文章或行業資訊,歡迎你們關注和轉發文章!

相關文章
相關標籤/搜索