1. 程式人生 > 實用技巧 >第五章:多執行緒

第五章:多執行緒

一、多執行緒概述

1.1、理解程式、程序、執行緒

1.1.1、程式

是為完成特定任務、用某種語言編寫的一組指令的集合。即指一段靜態的程式碼。

1.1.2、程序

程式的一次執行過程,或是正在執行的一個程式。

說明:

  程序作為資源分配的單位,系統在執行時會為每個程序分配不同的記憶體區域。

1.1.3、執行緒

程序可進一步細化為執行緒,是一個程式內部的一條執行路徑。

說明:

  執行緒作為排程和執行的單位,每個執行緒擁獨立的執行棧和程式計數器(pc),執行緒切換的開銷小。

1.1.4、補充說明

程序可以細化為多個執行緒。

每個執行緒,擁有自己獨立的:棧、程式計數器

多個執行緒,共享同一個程序中的結構:方法區、堆。

二、並行與併發

並行:多個CPU同時執行多個任務。比如:多個人同時做不同的事。

併發:一個CPU(採用時間片)同時執行多個任務。比如:秒殺、多個人做同一件事。

三、建立多執行緒的方式

3.1、繼承Thread類

3.1.1、具體步驟

  ① 建立一個繼承於Thread類的子類

  ② 重寫Thread類的run() --> 將此執行緒執行的操作宣告在run()中

  ③ 建立Thread類的子類的物件

  ④ 通過此物件呼叫start():①啟動當前執行緒 ② 呼叫當前執行緒的run()

3.1.2、問題說明

問題一:我們啟動一個執行緒,必須呼叫start(),不能呼叫run()的方式啟動執行緒。

問題二:如果再啟動一個執行緒,必須重新建立一個Thread子類的物件,呼叫此物件的start()。

3.1.3、程式碼

package com.zixue.java;

//1. 建立一個繼承於Thread類的子類
class MyThread extends Thread {
    //2. 重寫Thread類的run()
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            if(i % 2 == 0){
                System.out.println(Thread.currentThread().getName() 
+ ":" + i); } } } } public class ThreadTest { public static void main(String[] args) { //3. 建立Thread類的子類的物件 MyThread t1 = new MyThread(); //4.通過此物件呼叫start():①啟動當前執行緒 ② 呼叫當前執行緒的run() t1.start(); //問題一:我們不能通過直接呼叫run()的方式啟動執行緒。 // t1.run(); //問題二:再啟動一個執行緒,遍歷100以內的偶數。不可以還讓已經start()的執行緒去執行。會報IllegalThreadStateException // t1.start(); //我們需要重新建立一個執行緒的物件 MyThread t2 = new MyThread(); t2.start(); //如下操作仍然是在main執行緒中執行的。 for (int i = 0; i < 100; i++) { if(i % 2 == 0){ System.out.println(Thread.currentThread().getName() + ":" + i + "***********main()************"); } } } }

3.1.4、視窗賣票示例

建立三個視窗賣票,總票數為100張.使用繼承Thread類的方式。

package com.zixue.java;

/**
 * 存線上程安全問題:待解決
 */
class Window extends Thread{

    private static int ticket = 100;
    @Override
    public void run() {

        while(true){

            if(ticket > 0){
                System.out.println(getName() + ":賣票,票號為:" + ticket);
                ticket--;
            }else{
                break;
            }
        }
    }
}

public class WindowTest {
    public static void main(String[] args) {
        Window t1 = new Window();
        Window t2 = new Window();
        Window t3 = new Window();

        t1.setName("視窗1");
        t2.setName("視窗2");
        t3.setName("視窗3");

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

3.2、實現Runnable介面

3.2.1、具體步驟

  ① 建立一個實現了Runnable介面的類

  ② 實現類去實現Runnable中的抽象方法:run()

  ③ 建立實現類的物件

  ④ 將此物件作為引數傳遞到Thread類的構造器中,建立Thread類的物件

  ⑤ 通過Thread類的物件呼叫start()

3.2.2、程式碼

package com.zixue.java;

//1. 建立一個實現了Runnable介面的類
class MThread implements Runnable{

    //2. 實現類去實現Runnable中的抽象方法:run()
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            if(i % 2 == 0){
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
    }
}

public class ThreadTest1 {
    public static void main(String[] args) {
        //3. 建立實現類的物件
        MThread mThread = new MThread();
        //4. 將此物件作為引數傳遞到Thread類的構造器中,建立Thread類的物件
        Thread t1 = new Thread(mThread);
        t1.setName("執行緒1");
        //5. 通過Thread類的物件呼叫start():① 啟動執行緒 ②呼叫當前執行緒的run()-->呼叫了Runnable型別的target的run()
        t1.start();

        //再啟動一個執行緒,遍歷100以內的偶數
        Thread t2 = new Thread(mThread);
        t2.setName("執行緒2");
        t2.start();
    }
}

3.2.3、視窗賣票示例

package com.zixue.java;

class Window1 implements Runnable{

    private int ticket = 100;

    @Override
    public void run() {
        while(true){
            if(ticket > 0){
                System.out.println(Thread.currentThread().getName() + ":賣票,票號為:" + ticket);
                ticket--;
            }else{
                break;
            }
        }
    }
}

public class WindowTest1 {
    public static void main(String[] args) {
        Window1 w = new Window1();

        Thread t1 = new Thread(w);
        Thread t2 = new Thread(w);
        Thread t3 = new Thread(w);

        t1.setName("視窗1");
        t2.setName("視窗2");
        t3.setName("視窗3");

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

3.3、兩種方式的對比

開發中:優先選擇:實現Runnable介面的方式。

原因:

  ① 實現的方式沒類的單繼承性的侷限性

  ② 實現的方式更適合來處理多個執行緒有共享資料的情況。

聯絡:public class Thread implements Runnable

相同點:

  ① 兩種方式都需要重寫run(),將執行緒要執行的邏輯宣告在run()中。

  ② 目前兩種方式,要想啟動執行緒,都是呼叫的Thread類中的start()。

四、Thread類中的常用方法

  ① start():啟動當前執行緒;呼叫當前執行緒的run()

  ② run(): 通常需要重寫Thread類中的此方法,將建立的執行緒要執行的操作宣告在此方法中。

  ③ currentThread():靜態方法,返回執行當前程式碼的執行緒。

  ④ getName():獲取當前執行緒的名字

  ⑤ setName():設定當前執行緒的名字

  ⑥ yield():釋放當前cpu的執行權

  ⑦ join():線上程a中呼叫執行緒b的join(),此時執行緒a就進入阻塞狀態,直到執行緒b完全執行完以後,執行緒a才結束阻塞狀態。

  ⑧ stop():已過時。當執行此方法時,強制結束當前執行緒。

  ⑨ sleep(long millitime):讓當前執行緒“睡眠”指定的millitime毫秒。在指定的millitime毫秒時間內,當前執行緒是阻塞狀態。

  ⑩isAlive():判斷當前執行緒是否存活

4.1、執行緒優先順序涉及到的方法

4.1.1、表示優先順序的常量

MAX_PRIORITY:10

MIN _PRIORITY:1

NORM_PRIORITY:5 -->預設優先順序

4.1.2、如何獲取和設定當前執行緒的優先順序

  ① getPriority():獲取執行緒的優先順序

  ② setPriority(int p):設定執行緒的優先順序

4.1.3、說明

  高優先順序的執行緒要搶佔低優先順序執行緒cpu的執行權。但是隻是從概率上講,高優先順序的執行緒高概率的情況下被執行。並不意味著只有當高優先順序的執行緒執行完以後,低優先順序的執行緒才執行。

4.2、程式碼

package com.zixue.java;

class HelloThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            if(i % 2 == 0){

//                try {
//                    sleep(10);
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }

                System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().getPriority() + ":" + i);
            }

//            if(i % 20 == 0){
//                yield();
//            }
        }
    }

    public HelloThread(String name){
        super(name);
    }
}

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

        HelloThread h1 = new HelloThread("Thread:1");

//        h1.setName("執行緒一");
        //設定分執行緒的優先順序
        h1.setPriority(Thread.MAX_PRIORITY);

        h1.start();

        //給主執行緒命名
        Thread.currentThread().setName("主執行緒");
        Thread.currentThread().setPriority(Thread.MIN_PRIORITY);

        for (int i = 0; i < 100; i++) {
            if(i % 2 == 0){
                System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().getPriority() + ":" + i);
            }

//            if(i == 20){
//                try {
//                    h1.join();
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }
//            }

        }

//        System.out.println(h1.isAlive());

    }
}