自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Java 并發(fā)編程:本質(zhì)上只有一種創(chuàng)建線程的方法

開(kāi)發(fā)
在上一篇文章中,我們學(xué)習(xí)了操作系統(tǒng)中線程的基本概念。那么在 Java 中,我們?nèi)绾蝿?chuàng)建和使用線程呢?

上一篇文章中,我們學(xué)習(xí)了操作系統(tǒng)中線程的基本概念。那么在 Java 中,我們?nèi)绾蝿?chuàng)建和使用線程呢?首先請(qǐng)思考一個(gè)問(wèn)題。創(chuàng)建線程有多少種方法呢?大多數(shù)人會(huì)說(shuō)有 2 種、3 種或 4 種。很少有人會(huì)說(shuō)只有 1 種。讓我們看看他們實(shí)際指的是什么。最常見(jiàn)的答案是兩種創(chuàng)建線程的方法。讓我們先看看這兩種線程創(chuàng)建方法的代碼。

Thread 類和 Runnable 接口

(1) 繼承 Thread 類:第一種是繼承 Thread 類并重寫(xiě) run() 方法:

class SayHelloThread extends Thread {
    public void run() {
        System.out.println("hello world");
    }
}

public class ThreadJavaApp {
    public static void main(String[] args) {
        SayHelloThread sayHelloThread = new SayHelloThread();
        sayHelloThread.start();
    }
}

只有在主線程中創(chuàng)建 MyThread 的實(shí)例并調(diào)用 start() 方法,線程才會(huì)啟動(dòng)。

(2) 實(shí)現(xiàn) Runnable 接口:接下來(lái)看看 Runnable 接口:

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

實(shí)現(xiàn) Runnable 接口的 run() 方法,在這個(gè)方法里可以定義相應(yīng)的業(yè)務(wù)邏輯,不過(guò)我們還是需要通過(guò) Thread 類來(lái)啟動(dòng)線程。

class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("Hello Runnable");
    }
}

public class RunnableDemo {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        new Thread(myRunnable).start();
    }
}

從 Runnable 接口的定義中可以看出,Runnable 是一個(gè)函數(shù)式接口(JDK 1.8 及以上),這意味著我們可以使用 Java 8 的函數(shù)式編程來(lái)簡(jiǎn)化代碼:

public class RunnableDemo {
    public static void main(String[] args) {
        // Java 8 函數(shù)式編程,可以省略 MyThread 類的定義
        new Thread(() -> {
            System.out.println("Lambda 表達(dá)式實(shí)現(xiàn) Runnable");
        }).start();
    }
}

Callable、Future 和 FutureTask

一般來(lái)說(shuō),我們使用 Runnable 和 Thread 來(lái)創(chuàng)建一個(gè)新線程。然而,它們有一個(gè)缺點(diǎn),即 run 方法沒(méi)有返回值。有時(shí)我們希望啟動(dòng)一個(gè)線程來(lái)執(zhí)行任務(wù),并且在任務(wù)完成后有一個(gè)返回值。JDK 為我們提供了 Callable 接口來(lái)解決這個(gè)問(wèn)題。

(1) Callable 接口:Callable 與 Runnable 類似,它也是一個(gè)只有一個(gè)抽象方法的函數(shù)式接口。不同之處在于 Callable 提供的方法有返回值并支持泛型。

@FunctionalInterface
public interface Callable<V> {
    V call() throws Exception;
}

那么 Callable 通常如何使用呢?它是否與 Runnable 接口一樣,傳入 Thread 類呢?讓我們查看 JDK8 的 Java API,發(fā)現(xiàn)沒(méi)有使用 Callable 作為參數(shù)的構(gòu)造方法。

(2) Future 接口和 FutureTask 類:實(shí)際上它提供了 FutureTask 類來(lái)完成有返回值的異步計(jì)算。FutureTask 實(shí)現(xiàn)了 RunnableFuture 接口,而 RunnableFuture 接口同時(shí)繼承了 Runnable 接口和 Future 接口,因此可以傳入 Thread(Runable target)。

public interface RunnableFuture<V> extends Runnable, Future<V> {
  /**
     * Sets this Future to the result of its computation,
     * unless it has been cancelled.
     */
    void run();
}

Future 接口只有幾個(gè)簡(jiǎn)單的方法:

public abstract interface Future<V> {
    public abstract boolean cancel(boolean paramBoolean);
    public abstract boolean isCancelled();
    public abstract boolean isDone();
    public abstract V get() throws InterruptedException, ExecutionException;
    public abstract V get(long paramLong, TimeUnit paramTimeUnit)
            throws InterruptedException, ExecutionException, TimeoutException;
}

每個(gè)方法的功能如下:

  • get():等待計(jì)算完成并返回結(jié)果。
  • get(long paramLong, TimeUnit paramTimeUnit):等待設(shè)定的時(shí)間。如果在設(shè)定時(shí)間內(nèi)計(jì)算完成,則返回結(jié)果,否則拋出 TimeoutException。
  • isDone:如果任務(wù)完成,則返回 true。完成可能是由于正常終止、異?;蛉∠?。在所有這些情況下,方法都將返回 true。
  • isCancelled:如果此任務(wù)在正常完成之前被取消,則返回 true。
  • cancel:嘗試取消線程的執(zhí)行。請(qǐng)注意,這是嘗試取消,不一定能成功取消。因?yàn)槿蝿?wù)可能已經(jīng)完成、被取消或由于其他一些因素而無(wú)法取消,所以取消可能會(huì)失敗。布爾類型的返回值表示取消是否成功。參數(shù) paramBoolean 表示是否通過(guò)中斷線程來(lái)取消線程執(zhí)行。

有時(shí)使用 Callable 而不是 Runnable 是為了能有取消任務(wù)的能力。如果使用 Future 只是為了可以取消任務(wù)但不提返回結(jié)果可以聲明 Future<? >的類型,并將底層任務(wù)的結(jié)果返回為 null。

你可能會(huì)問(wèn),為什么要有 FutureTask 類呢?前面說(shuō)過(guò) Future 只是一個(gè)接口,其方法 cancel、get、isDone 等如果自己實(shí)現(xiàn)會(huì)非常復(fù)雜。因此 JDK 為我們提供了 FutureTask 類供我們直接使用。

FutureTask 需要與 Callable 結(jié)合使用來(lái)完成有返回值的異步計(jì)算,這里看一個(gè)其使用的簡(jiǎn)單示例:

class MyCallable implements Callable<Integer> {
    /**
     * 計(jì)算 1 到 4 的總和
     * @return
     */
    @Override
    public Integer call() {
        int res = 0;
        for (int i = 0; i < 5; i++) {
            res += i;
        }
        return res;
    }
}

publicclass CallableDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 1. 生成 MyCallable 的實(shí)例
        MyCallable myCallable = new MyCallable();
        // 2. 通過(guò) myCallable 創(chuàng)建 FutureTask 對(duì)象
        FutureTask<Integer> futureTask = new FutureTask<>(myCallable);
        // 3. 通過(guò) FutureTask 創(chuàng)建 Thread 對(duì)象
        Thread t = new Thread(futureTask);
        // 4. 啟動(dòng)線程
        t.start();
        // 5. 獲取計(jì)算結(jié)果
        Integer res = futureTask.get();
        System.out.println(res);
    }
}

輸出:

10

為什么只有一種實(shí)現(xiàn)線程的方法?

我相信你對(duì)這個(gè)問(wèn)題基本上有了答案。無(wú)論你是實(shí)現(xiàn) Runnable 接口、實(shí)現(xiàn) Callable 接口還是直接繼承 Thread 類來(lái)創(chuàng)建線程,最終都是創(chuàng)建一個(gè) Thread 實(shí)例來(lái)啟動(dòng)線程,即 new Thread(),只是創(chuàng)建的形式不同而已!

實(shí)際上,線程不僅可以通過(guò)上述形式創(chuàng)建,還可以通過(guò)內(nèi)置的工具類(如線程池)來(lái)創(chuàng)建,后續(xù)文章將單獨(dú)介紹。

實(shí)現(xiàn) Runnable 接口優(yōu)于繼承 Thread 類

要實(shí)現(xiàn)一個(gè)沒(méi)有返回值的線程類,你可以繼承 Thread 類或?qū)崿F(xiàn) Runnable 接口,它們之間有什么優(yōu)缺點(diǎn)呢?

(1) Thread 類的優(yōu)點(diǎn):

  • 簡(jiǎn)單直觀:由于繼承關(guān)系,代碼結(jié)構(gòu)相對(duì)簡(jiǎn)單易懂。
  • 線程控制:可以直接使用 Thread 類的方法來(lái)控制線程的狀態(tài),如啟動(dòng)、暫停、停止等。

(2) Thread 類的缺點(diǎn):

  • 單繼承限制:由于 Java 不支持多重繼承,使用 Thread 類限制了類的擴(kuò)展。
  • 代碼耦合:線程類和線程執(zhí)行邏輯緊密耦合,不利于代碼復(fù)用和維護(hù)。

(3) Runnable 接口的優(yōu)點(diǎn):

  • 更好的代碼復(fù)用:由于它是一個(gè)接口,可以將線程的執(zhí)行邏輯與其他類分離,以實(shí)現(xiàn)代碼復(fù)用。
  • 靈活性:可以同時(shí)實(shí)現(xiàn)多個(gè)接口,避免單繼承的限制。
  • 更好的可擴(kuò)展性:接口使得在不影響現(xiàn)有代碼的情況下擴(kuò)展線程功能變得容易。即面向接口編程的原則。

(4) Runnable 接口的缺點(diǎn):

  • 代碼稍微復(fù)雜一些:需要?jiǎng)?chuàng)建一個(gè)實(shí)現(xiàn) Runnable 接口的類并實(shí)現(xiàn) run() 方法,然后由 Thread 類驅(qū)動(dòng)。
  • 沒(méi)有線程控制方法:不能直接使用 Thread 類的線程控制方法,需要通過(guò) Thread 對(duì)象調(diào)用它們。

所以,綜合考慮,通常建議使用 Runnable 接口的實(shí)現(xiàn)來(lái)創(chuàng)建線程,以獲得更好的代碼可復(fù)用性和可擴(kuò)展性。

Thread 的 start() 方法

在程序中調(diào)用 start() 方法后,虛擬機(jī)首先為我們創(chuàng)建一個(gè)線程,然后等待直到這個(gè)線程獲得時(shí)間片,才會(huì)調(diào)用 run() 方法執(zhí)行具體邏輯。

請(qǐng)注意,start() 方法不能多次調(diào)用。第一次調(diào)用 start() 方法后,再次調(diào)用會(huì)拋出 IllegalThreadStateException 異常。

你可以簡(jiǎn)單看一下 start() 方法的源代碼。實(shí)際上,實(shí)際工作是由 start0() 完成的,它是一個(gè)本地方法,我添加了一些注釋,以便你更容易理解。

public synchronized void start() {
    /**
     * 零狀態(tài)值對(duì)應(yīng)于狀態(tài) NEW。
     */
    if (threadStatus!= 0)
        thrownew IllegalThreadStateException();
    group.add(this); // 將其所屬的線程組加上該線程
    boolean started = false;
    try {
        start0(); // 本地方法調(diào)用實(shí)際創(chuàng)建線程的底層方法。
        started = true;
    } finally {
        try {
            if (!started) {
                group.threadstartFailed(this);
            }
        } catch (Throwable ignore) {
            /* 什么也不做。如果 start0 拋出了 Throwable,那么
             * 它將在調(diào)用棧中向上傳遞 */
        }
    }
}

ThreadGroup 的概念將在后續(xù)文章中介紹。你可以在這里忽略它。

Thread 類的幾個(gè)常用方法

這里我們簡(jiǎn)要提及 Thread 類的幾個(gè)常用方法,先熟悉一下。后續(xù)文章將根據(jù)具體使用場(chǎng)景詳細(xì)介紹:

  • currentThread():靜態(tài)方法,返回當(dāng)前正在執(zhí)行的線程對(duì)象的引用;
  • sleep():靜態(tài)方法,使當(dāng)前線程睡眠指定的時(shí)間;
  • yield():表示當(dāng)前線程愿意放棄對(duì)當(dāng)前處理器的占用。請(qǐng)注意,即使當(dāng)前線程調(diào)用了 yield() 方法,它仍然有可能繼續(xù)運(yùn)行;
  • join():使當(dāng)前線程等待另一個(gè)線程完成執(zhí)行后再繼續(xù),內(nèi)部調(diào)用是通過(guò) Object 類的 wait 方法實(shí)現(xiàn)的;

好了,這次就到這里,下次再見(jiàn)!

責(zé)任編輯:趙寧寧 來(lái)源: 程序猿技術(shù)充電站
相關(guān)推薦

2012-08-13 10:26:53

云計(jì)算云服務(wù)

2022-06-06 15:44:24

大數(shù)據(jù)數(shù)據(jù)分析思維模式

2022-11-22 11:18:38

Java虛擬線程

2025-02-14 10:25:53

2020-12-14 06:43:02

并發(fā)編程JDK

2023-10-24 09:03:05

C++編程

2024-04-01 08:38:57

Spring@AspectAOP

2023-12-04 08:21:18

虛擬線程Tomcat

2025-02-10 08:43:31

Java異步編程

2010-12-06 16:57:13

FreeBSDLinux

2025-02-17 00:00:25

Java并發(fā)編程

2025-02-19 00:05:18

Java并發(fā)編程

2011-02-23 09:35:25

Eclipse遠(yuǎn)程調(diào)試

2011-12-29 13:31:15

Java

2015-10-14 17:54:01

容器虛擬機(jī)云服務(wù)

2023-07-18 18:10:04

2024-12-30 08:20:29

程序并發(fā)任務(wù)線程

2015-03-13 11:23:21

編程編程超能力編程能力

2012-07-30 09:58:53

2012-11-01 13:41:25

編程語(yǔ)言BasicPerl
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)