【十八掌●基本功篇】第一掌:Java之多線程--1-一些概念

這一篇博文是【大數據技術●降龍十八掌】系列文章的其中一篇,點擊查看目錄:這裏寫圖片描述大數據技術●降龍十八掌


系列文章:
【十八掌●基本功篇】第一掌:Java之IO
【十八掌●基本功篇】第一掌:Java之多線程–1-一些概念
【十八掌●基本功篇】第一掌:Java之多線程–2-join、同步、死鎖、等待

1、操作系統教程中對進程的描述

進程的兩個基本特徵
(1) 進程是一個擁有資源的獨立單元
(2) 進程是一個被操作系統獨立調度和執行的基本單元。
進程的特點
(1) 動態特徵:進程是程序的一次執行過程,是臨時的,有生命期的,是動態產生,動態消亡的;
(2) 併發特徵:任何進程都可以同其他進程一起併發執行;
(3) 獨立特徵:進程是系統進行資源分配和調度的一個獨立單位;
(4) 結構特徵:進程由程序、數據和進程控制塊三部分組成。
(5) 異步特徵

2、進程切換過程

比如進程A切換到進程B,過程如下:
(1) 保存被中斷進程A的處理器現場信息
(2) 修改被中斷進程A的進程控制塊信息:如進程狀態等。
(3) 把被中斷進程A的進程控制塊加入到有關隊列
(4) 選擇下一個允許使用處理器資源的就緒狀態的進程B
(5) 修改被選中進程B的進程控制塊有關信息:比如進程狀態
(6) 根據被選中的進程B的設置操作系統用到的地址轉換信息和存儲保護信息
(7) 根據被選中進程B恢復處理器現場。

3、線程的優點

(1) 創建時間短
(2) 終止時間開銷少
(3) 切換快
(4) 通訊效率高

4、線程創建方法

線程的創建方法有兩種:

(1)繼承java.lang.Thread類

Thread類有一個run方法,子類應該重寫這個方法,在這個run方法裏寫新建的線程要做的業務邏輯代碼。

/**
 * Created by 鳴宇淳 on 2017/12/7.
 */
public class HelloThread extends Thread {

    @Override
    public void run() {
        //子線程,Thread.currentThread().getName(),獲取當前線程爲Thread-n
        for (int n = 0; n < 100; n++) {
            System.out.println("當前線程" + Thread.currentThread().getName() + "==>" + n);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


public class MyThread {

    public static void main(String[] args) {
        //main方法裏是主線程
        //Thread.currentThread().getName() 是獲取當前線程名稱,爲main
        System.out.println("當前線程" + Thread.currentThread().getName());

        //第一個線程
        HelloThread t1 = new HelloThread();

        //第二個線程
        HelloThread t2 = new HelloThread();

        //啓動線程的方法是start,而不是run。
        //1、如果調用run(),相當於調用一個普通類的方法
        //2、如果調用start(),當做一個子線程來啓動
        t1.start();
        t2.start();
        System.out.println("正在打印...");
    }
}

(2)實現接口java.lang.Runnable

實現了這個接口的類,調用run方法來創建一個線程。
java裏類只能繼承於一個父類,當子類需要繼承於其他類時,使用繼承java.lang.Thread類的方式來實現多線程就不方便了,可以使用實現java.lang.Runnable接口的方式來實現多線程。
實際上,Thread類也是繼承了Runnable接口。
這種方式創建子線程更加的靈活,所以推薦使用這種方式。

實例:

/**
 * Created by 鳴宇淳 on 2017/12/7.
 */
public class HelloRunner implements Runnable {
    public void run() {
        //子線程,Thread.currentThread().getName(),獲取當前線程爲Thread-n
        for (int n = 0; n < 100; n++) {
            System.out.println("當前線程" + Thread.currentThread().getName() + "==>" + n);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class MyThreadSecond {
    public static void main(String[] args) {

        //實例化一個線程執行的對象
        HelloRunner runner=new HelloRunner();

        //定義線程,指定線程要執行的對象,
        // 第二個參數是給線程起一個名稱(可以省略)
        Thread t1=new Thread(runner,"T-1");
        Thread t2=new Thread(runner,"T-2");

        //啓動子線程
        t1.start();
        t2.start();

        System.out.println("主線程" + Thread.currentThread().getName());
    }
}

5、Start方法做的事情

在線程對象上調用start方法之前,線程是處於新狀態,新狀態是指有了一個Thread對象,但是還有一個對應的線程。
當調用start方法後,線程就變爲了可運行狀態,請注意:可運行狀態並不是實際運行,是否可以運行還需要JVM的調度。

6、線程的狀態轉換

這裏寫圖片描述

新狀態
線程對象已經創建,還沒有調用start()方法。
可運行狀態
當線程有資格運行,但是調度程序還沒有將它選定爲運行線程時所處於的狀態。當start()方法調用後,線程就進入可以運行狀態。
當線程在運行,或者從阻塞、等待、睡眠狀態中,也可以返回可運行狀態。
運行狀態
線程調度程序從可運行池中選擇一個線程運行,這個被選中的線程就變爲了運行狀態。
這個也是線程進入運行狀態的唯一方式。
等待/阻塞/睡眠
線程沒有條件運行,當滿足條件後就可以返回可運行狀態。
死亡狀態
當線程的run()方法完成時就認爲線程死亡了。線程死亡了,但是線程對象有可能還存在,線程死亡不能復生,如果在線程對象上調用start()方法,會拋出異常。

7、線程的優先級和讓步

線程是有優先級的,JVM線程調度程序是基於優先級的搶佔調用機制,優先級越大的線程,被選中的概率更大,但是不能保證是嚴格按照優先級的順序來分別執行的。

線程的讓步是暫停當前線程,讓調度程序來確定執行其他線程。線程的讓步是通過Thread.yield()來實現的。yield()方法是讓當前運行線程回到可以運行狀態,以讓線程調度程序選擇其他線程進行執行,但是實際上yield()無法保證達到讓步的目的,因爲執行yield()的線程還有可能被線程調度程序再次選中。

public class MyRunner implements Runnable {
    public void run() {

        for (int n = 0; n < 100; n++) {
            System.out.println(Thread.currentThread().getName() + ":" + n);

            //打印一次,就讓步一次,可以模擬一個線程執行一次的效果
            //也不一定嚴格的一個線程執行一次
            Thread.yield();
        }

    }
}

public class MyYield {
    public static void main(String[] args) {

        MyRunner runner=new MyRunner();

        Thread t1=new Thread(runner,"線程1");
        Thread t2=new Thread(runner,"線程2");

        t1.start();
        t2.start();
    }
}

輸出爲


線程10
線程20
線程11
線程21
線程12
線程22
線程13
線程23
線程14
線程24
.......
.......

可以從結果來看,基本上是一個線程執行一次。
但是yield()方法讓步可控性較差,一般不使用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章