一文搞懂java中的執行緒如何理解

2022-07-18 14:00:20
本篇文章給大家帶來了關於的相關知識,其中主要整理了執行緒的相關問題,執行緒(thread)是一個程式內部的一條執行路徑,我們所熟悉的main方法其實就是一條單獨執行路徑,若程式中只有一條執行路徑那麼這個程式就是單執行緒程式,下面一起來看一下,希望對大家有幫助。

推薦學習:《》

執行緒(thread)是一個程式內部的一條執行路徑,我們所熟悉的main方法其實就是一條單獨執行路徑,若程式中只有一條執行路徑那麼這個程式就是單執行緒程式;既然有單執行緒,那麼也相對的會有多執行緒,字面意思可以理解為「相對單執行緒從軟硬體上多條執行流程的技術」,多執行緒的好處是提高CPU的利用率。 在多執行緒程式中,一個執行緒必須等待的時候,CPU可以執行其它的執行緒而不是等待,大大提高程式的效率。

多執行緒的建立

方式一:繼承Thread類

方式一建立過程:

  • 定義一個子類MyThread繼承執行緒類java.lang.Thread,重寫run()方法;

  • 建立MyThread類的物件;

  • 呼叫執行緒物件的start()方法啟動執行緒(啟動後仍然執行run()方法);

    public class ThreadDemo01 {
        public static void main(String[] args) {
            MyThread myThread1 = new MyThread();
            myThread1.start();
            for (int i = 0; i < 3; i++) {
                System.out.println("主執行緒正在執行~~");
            }
        }
    }
    class MyThread extends Thread{
        @Override
        public void run() {
            for (int i = 0; i < 3; i++) {
                System.out.println("子執行緒正在執行~~");
            }
    
        }
    }
    //輸出結果(不唯一):
    //主執行緒正在執行~~
    //主執行緒正在執行~~
    //主執行緒正在執行~~
    //子執行緒正在執行~~
    //子執行緒正在執行~~
    //子執行緒正在執行~~
  • 在以上程式碼中共有兩個執行緒在執行,分別是main方法的主執行緒和執行緒物件mythread呼叫start()啟動的子執行緒。不過輸出結果為什麼會不唯一的呢?原因是因為兩個執行緒在執行過程中會發生CPU的搶佔,誰先搶到誰將優先執行。

那麼我們為什麼不直接使用執行緒物件呼叫run()方法呢?若直接呼叫run()則只是普通的呼叫方法,即單執行緒,而start()方法則是用來啟動的子執行緒的,由此才能出現多執行緒。

方式一優缺點:

  • 優點:編碼簡單;
  • 缺點:執行緒類已經繼承Thread,無法繼承其他類,不利於擴充套件;

方式二: 實現Runnable介面

方式二建立過程:

1、定義一個執行緒任務類MyRunnable實現Runnable介面,重寫run()方法;

2、建立MyRunnable物件;

3、把MyRunnable任務物件交給Thread處理;

4、呼叫執行緒物件的start()方法啟動執行緒;

Thread構造器方法
public Thread (String name)可以為當前執行緒指定名稱
public Thread (Runnable target)封裝Runnable物件成為執行緒物件
public Thread (Runnable target ,String name)封裝Runnable物件成為執行緒物件,並指定執行緒名稱
public class ThreadDemo02 {
    public static void main(String[] args) {
        MyRunnable target = new MyRunnable();
        Thread thread = new Thread(target);
        thread.start();
        for (int i = 0; i < 3; i++) {
            System.out.println("主執行緒正在執行~~");
        }
    }
}
class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
            System.out.println("子執行緒正在執行~~");
        }

    }
}
//輸出結果(不唯一):
//主執行緒正在執行~~
//子執行緒正在執行~~
//子執行緒正在執行~~
//子執行緒正在執行~~
//主執行緒正在執行~~
//主執行緒正在執行~~

該程式碼與方式一的不同之處在於需要將MyRunnable任務物件封裝在Thread中,其他的地方是基本上是沒有變化的。

方式二優缺點:

優點:執行緒任務類只是實現介面,可以繼續繼承類和實現介面,擴充套件性強;

缺點:程式設計多一層物件包裝,如果執行緒有執行結果是不可以直接返回的。

接下來我們同樣使用實現Runnable介面(匿名內部類形式)來實現多執行緒的建立:

1、建立Runnable匿名內部類物件;

2、交給Thread處理;

3、呼叫執行緒物件的start()啟動執行緒;

//正常版:
public class ThreadDemo01 {
    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 3; i++) {
                    System.out.println("子執行緒正在執行~~");
                }
            }
        });
        thread.start();
        for (int i = 0; i < 3; i++) {
            System.out.println("主執行緒正在執行~~");
        }
    }
}

//lambda簡化版:
new Thread(()-> {
                for (int i = 0; i < 3; i++) {
                    System.out.println("子執行緒正在執行~~");
                }
        }).start();

該種方法從本質上其實並沒有太大的區別只不過是一個需要建立執行緒物件,而另一個則是通過匿名內部類實現的多執行緒。並且該塊程式碼也可以通過lambda表示式進行精簡,不知道大家是否還對這個知識點有印象呢?若忘記了可以看一下這篇文章:Java中的lambda表示式如何理解——精簡

方式三:實現Callable介面

在學習過前面兩種建立多執行緒的方式以後,我們會發現存在一個問題:1、重寫的run()方法不能直接返回結果;2、不適合需要返回執行緒執行結果的業務場景。因此,我們需要第三種方式來解決這些問題。

方式三建立過程:

1、定義類實現Callable介面,重寫call()方法,封裝要做的事情;

2、用FutureTask把Callable物件封裝成執行緒任務物件;

3、把執行緒任務物件交給Thread處理;

4、呼叫Thread的start()方法啟動執行緒,執行任務;

5、執行緒執行完畢後,通過FutureTask的get()方法獲取任務執行的結果。

方法名稱說明
public FutureTask<>(Callable call)把Callable物件封裝成FutureTask物件
public V get() throws Exception獲取執行緒執行call方法返回的結果
public class ThreadDemo03 {
    public static void main(String[] args) throws Exception {
        MyCallable myCallable = new MyCallable();
        FutureTask<String> futureTask = new FutureTask<>(myCallable);
        Thread thread = new Thread(futureTask);
        thread.start();
        int sum= 0;
        for (int i = 0; i < 3; i++) {
            sum+=i;
        }
        System.out.println(sum);
        String s =futureTask.get();
        System.out.println(s);
    }
}
class MyCallable implements Callable<String > {
    @Override
    public String call(){
        int sum=0;
        for (int i = 0; i < 3; i++) {
            sum+=i;
        }
        return "子執行緒計算結果:"+sum;
    }
}
//輸出結果:
//3
//子執行緒計算結果:3

方式三優缺點:

優點:

執行緒任務類只是實現介面,可以繼續繼承類和實現介面,擴充套件性強;

可以線上程執行完畢後去獲取 執行緒執行的結果;

缺點:

編碼複雜一點;

總結

方式優點缺點
繼承Thread類程式設計比較簡單,可以直接使用Thread類中的方法擴充套件性較差,不能再繼承其他的類,不能返回執行緒執行的結果
實現Runnable介面擴充套件性強,實現該介面的同時還可以繼承其他的類程式設計相對複雜,不能返回執行緒執行的結果
實現Callable介面擴充套件性強,實現該介面的同時還可以繼承其他的類,可以得到執行緒的執行結果程式設計相對複雜

常用方法

Thread獲取和設定執行緒名稱

方法名稱說明
String getName()獲取當前執行緒的名稱,預設執行緒名稱是Thread-索引
void setName(String name)

將此執行緒更改為指定的名稱,通過構造器也可以設定執行緒名稱

簡單地通過一段程式碼讓大家能夠清晰地瞭解這個程式碼該如何使用:

public class ThreadDemo04 {
    public static void main(String[] args) throws Exception {
        thread thread1 = new thread();
        thread1.setName("1號子執行緒");
        thread1.start();
        thread thread2 = new thread();
        thread2.setName("2號子執行緒");
        thread2.start();
    }
}
class thread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
            System.out.println(this.getName()+"正在執行任務"+i);
        }
    }
}
//輸出結果:
//2號子執行緒正在執行任務0
//1號子執行緒正在執行任務0
//2號子執行緒正在執行任務1
//1號子執行緒正在執行任務1
//2號子執行緒正在執行任務2
//1號子執行緒正在執行任務2

Thread類的執行緒休眠方法

方法名稱說明
public static void sleep(long time) 讓當前執行緒休眠指定的時間後再繼續執行,單位為毫秒
public class ThreadDemo05 {
    public static void main(String[] args) throws Exception {
        for (int i = 0; i < 5; i++) {
            System.out.println(i);
            if (i==3){
                Thread.sleep(5000);
            }
        }
    }
}
//輸出結果:
//1
//2
//3
//在輸出過3以後,等待5秒之後再進行輸出
//4

推薦學習:《》

以上就是一文搞懂java中的執行緒如何理解的詳細內容,更多請關注TW511.COM其它相關文章!