亚洲国产日韩欧美一区二区三区,精品亚洲国产成人av在线,国产99视频精品免视看7,99国产精品久久久久久久成人热,欧美日韩亚洲国产综合乱

目錄
1. 創(chuàng)建多個(gè)線(xiàn)程" >1. 創(chuàng)建多個(gè)線(xiàn)程
#2. 指定執(zhí)行緒名稱(chēng)" >#2. 指定執(zhí)行緒名稱(chēng)
1. 創(chuàng)建線(xiàn)程任務(wù)" >1. 創(chuàng)建線(xiàn)程任務(wù)
2. 創(chuàng)建可運(yùn)行類(lèi)" >2. 創(chuàng)建可運(yùn)行類(lèi)
3. lambda方式創(chuàng)建線(xiàn)程任務(wù)" >3. lambda方式創(chuàng)建線(xiàn)程任務(wù)
lambda 方式改造" >lambda 方式改造
1. 指定時(shí)間點(diǎn)執(zhí)行" >1. 指定時(shí)間點(diǎn)執(zhí)行
2.間隔時(shí)間重復(fù)執(zhí)行" >2.間隔時(shí)間重復(fù)執(zhí)行
首頁(yè) Java Java基礎(chǔ) java實(shí)作多執(zhí)行緒的幾種方式

java實(shí)作多執(zhí)行緒的幾種方式

Jan 04, 2023 pm 03:52 PM
java 多執(zhí)行緒

實(shí)現(xiàn)多線(xiàn)程的方式:1、繼承Thread類(lèi),透過(guò)JDK提供的Thread類(lèi),重寫(xiě)Thread類(lèi)別的run方法即可;2、實(shí)作Runnable接口,Runnable是一個(gè)「@FunctionalInterface」函數(shù)式接口,也就意味了可以利用JDK8提供的lambda的方式來(lái)創(chuàng)建線(xiàn)程任務(wù);3、使用內(nèi)部類(lèi)的方式;4、利用定時(shí)器;5、帶返回值的線(xiàn)程實(shí)現(xiàn)方式;6、基於線(xiàn)程池實(shí)現(xiàn)多線(xiàn)程。

java實(shí)作多執(zhí)行緒的幾種方式

本教學(xué)操作環(huán)境:windows7系統(tǒng)、java8版、DELL G3電腦。

多執(zhí)行緒的形式上實(shí)作方式主要有兩種,一種是繼承Thread類(lèi),一種是實(shí)作Runnable介面。本質(zhì)上實(shí)作方式都是來(lái)實(shí)作執(zhí)行緒任務(wù),然後啟動(dòng)執(zhí)行緒執(zhí)行執(zhí)行緒任務(wù)(這裡的執(zhí)行緒任務(wù)其實(shí)就是run方法)。這裡所說(shuō)的6種,其實(shí)都是在以上兩種的基礎(chǔ)上的一些變形。

以下分別就這6中實(shí)作方式一一介紹。

第一個(gè)方式:繼承Thread類(lèi)別

#萬(wàn)物皆對(duì)象,那麼執(zhí)行緒也是對(duì)象,物件就應(yīng)該能夠抽取其公共特性封裝成為類(lèi),使用類(lèi)別可以實(shí)例化多個(gè)對(duì)象,那麼實(shí)作執(zhí)行緒的第一種方式就是繼承Thread類(lèi)別。繼承Thread類(lèi)別是最簡(jiǎn)單的一種實(shí)作執(zhí)行緒的方式,透過(guò)JDK提供的Thread類(lèi),重寫(xiě)Thread類(lèi)別的run方法即可,那麼當(dāng)執(zhí)行緒啟動(dòng)的時(shí)候,就會(huì)執(zhí)行run方法體的內(nèi)容。程式碼如下:

package com.kingh.thread.create;

/**
 * 繼承Thread類(lèi)的方式創(chuàng)建線(xiàn)程
 *
 * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a>
 * @version 1.0
 * @date 2019/3/13 19:19
 */
public class CreateThreadDemo1 extends Thread {

    public CreateThreadDemo1() {
        // 設(shè)置當(dāng)前線(xiàn)程的名字
        this.setName("MyThread");
    }

    @Override
    public void run() {
        // 每隔1s中輸出一次當(dāng)前線(xiàn)程的名字
        while (true) {
            // 輸出線(xiàn)程的名字,與主線(xiàn)程名稱(chēng)相區(qū)分
            printThreadInfo();
            try {
                // 線(xiàn)程休眠一秒
                Thread.sleep(1000);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static void main(String[] args) throws Exception {
        // 注意這里,要調(diào)用start方法才能啟動(dòng)線(xiàn)程,不能調(diào)用run方法
        new CreateThreadDemo1().start();

        // 演示主線(xiàn)程繼續(xù)向下執(zhí)行
        while (true) {
            printThreadInfo();
            Thread.sleep(1000);
        }
    }

    /**
     * 輸出當(dāng)前線(xiàn)程的信息
     */
    private static void printThreadInfo() {
        System.out.println("當(dāng)前運(yùn)行的線(xiàn)程名為: " + Thread.currentThread().getName());
    }
}

執(zhí)行結(jié)果如下

當(dāng)前運(yùn)行的線(xiàn)程名為: main
當(dāng)前運(yùn)行的線(xiàn)程名為: MyThread
當(dāng)前運(yùn)行的線(xiàn)程名為: main
當(dāng)前運(yùn)行的線(xiàn)程名為: MyThread
當(dāng)前運(yùn)行的線(xiàn)程名為: MyThread
當(dāng)前運(yùn)行的線(xiàn)程名為: main

這裡要注意,在啟動(dòng)執(zhí)行緒的時(shí)候,並不是呼叫執(zhí)行緒類(lèi)別的run方法,而是呼叫了執(zhí)行緒類(lèi)別的start方法。那我們能不能呼叫run方法呢?答案是肯定的,因?yàn)閞un方法是一個(gè)public聲明的方法,因此我們是可以調(diào)用的,但是如果我們調(diào)用了run方法,那麼這個(gè)方法將會(huì)作為一個(gè)普通的方法被調(diào)用,並不會(huì)開(kāi)啟線(xiàn)程。這裡實(shí)際上是採(cǎi)用了設(shè)計(jì)模式中的模板方法模式,Thread類(lèi)別作為模板,而run方法是在變化的,因此放到子類(lèi)別來(lái)實(shí)現(xiàn)。

1. 創(chuàng)建多個(gè)線(xiàn)程

#上面的範(fàn)例中除了我們創(chuàng)建的一個(gè)線(xiàn)程以外其實(shí)還有一個(gè)主線(xiàn)程也在執(zhí)行。那麼除了這兩個(gè)線(xiàn)程以外還有沒(méi)有其他的線(xiàn)程在執(zhí)行了呢,其實(shí)是有的,比如我們看不到的垃圾回收線(xiàn)程,也在默默的執(zhí)行。這裡我們不去考慮有多少個(gè)線(xiàn)程在執(zhí)行,上面我們自己創(chuàng)建了一個(gè)線(xiàn)程,那麼能不能多創(chuàng)建幾個(gè)一起執(zhí)行呢,答案是肯定的。一個(gè)Thread類(lèi)就是一個(gè)線(xiàn)程對(duì)象,那麼多創(chuàng)建幾個(gè)Thread類(lèi),並調(diào)用其start方法就可以啟動(dòng)多個(gè)線(xiàn)程了。程式碼如下

package com.kingh.thread.create;

/**
 * 創(chuàng)建多個(gè)線(xiàn)程同時(shí)執(zhí)行
 *
 * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a>
 * @version 1.0
 * @date 2019/3/18 9:46
 */
public class CreateMultiThreadDemo2 extends Thread {

    public CreateMultiThreadDemo2(String name) {
        // 設(shè)置當(dāng)前線(xiàn)程的名字
        this.setName(name);
    }

    @Override
    public void run() {
        // 每隔1s中輸出一次當(dāng)前線(xiàn)程的名字
        while (true) {
            // 輸出線(xiàn)程的名字,與主線(xiàn)程名稱(chēng)相區(qū)分
            printThreadInfo();
            try {
                // 線(xiàn)程休眠一秒
                Thread.sleep(1000);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static void main(String[] args) throws Exception {
        // 注意這里,要調(diào)用start方法才能啟動(dòng)線(xiàn)程,不能調(diào)用run方法
        new CreateMultiThreadDemo2("MyThread-01").start();

        // 創(chuàng)建多個(gè)線(xiàn)程實(shí)例,同時(shí)執(zhí)行
        new CreateMultiThreadDemo2("MyThread-02").start();

        // 演示主線(xiàn)程繼續(xù)向下執(zhí)行
        while (true) {
            printThreadInfo();
            Thread.sleep(1000);
        }
    }

    /**
     * 輸出當(dāng)前線(xiàn)程的信息
     */
    private static void printThreadInfo() {
        System.out.println("當(dāng)前運(yùn)行的線(xiàn)程名為: " + Thread.currentThread().getName());
    }
}

運(yùn)行結(jié)果如下

當(dāng)前運(yùn)行的線(xiàn)程名為: main
當(dāng)前運(yùn)行的線(xiàn)程名為: MyThread-02
當(dāng)前運(yùn)行的線(xiàn)程名為: MyThread-01
當(dāng)前運(yùn)行的線(xiàn)程名為: main
當(dāng)前運(yùn)行的線(xiàn)程名為: MyThread-01
當(dāng)前運(yùn)行的線(xiàn)程名為: MyThread-02
當(dāng)前運(yùn)行的線(xiàn)程名為: main

#2. 指定執(zhí)行緒名稱(chēng)

可以看到,透過(guò)建立多個(gè)Thread類(lèi),並且呼叫其start方法,啟動(dòng)了多個(gè)執(zhí)行緒。每個(gè)執(zhí)行緒都有自己的名字,在上述程式碼中,分別給創(chuàng)建的執(zhí)行緒指定了MyThread-01和MyThread-02這個(gè)名字,然後在建構(gòu)方法中透過(guò)呼叫父類(lèi)別的setName方法來(lái)賦予執(zhí)行緒名字賦值。如果不指定執(zhí)行緒名字,系統(tǒng)會(huì)預(yù)設(shè)指定執(zhí)行緒名,命名規(guī)則是Thread-N的形式。但是為了排查問(wèn)題方便,建議在創(chuàng)建線(xiàn)程的時(shí)候指定一個(gè)合理的線(xiàn)程名字。下面的程式碼是不使用執(zhí)行緒名稱(chēng)的樣子

package com.kingh.thread.create;

/**
 * 創(chuàng)建多個(gè)線(xiàn)程同時(shí)執(zhí)行,使用系統(tǒng)默認(rèn)線(xiàn)程名
 *
 * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a>
 * @version 1.0
 * @date 2019/3/18 9:46
 */
public class CreateMultiThreadDemo3 extends Thread {

    @Override
    public void run() {
        // 每隔1s中輸出一次當(dāng)前線(xiàn)程的名字
        while (true) {
            // 輸出線(xiàn)程的名字,與主線(xiàn)程名稱(chēng)相區(qū)分
            printThreadInfo();
            try {
                // 線(xiàn)程休眠一秒
                Thread.sleep(1000);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static void main(String[] args) throws Exception {
        // 注意這里,要調(diào)用start方法才能啟動(dòng)線(xiàn)程,不能調(diào)用run方法
        new CreateMultiThreadDemo3().start();

        // 創(chuàng)建多個(gè)線(xiàn)程實(shí)例,同時(shí)執(zhí)行
        new CreateMultiThreadDemo3().start();

        // 演示主線(xiàn)程繼續(xù)向下執(zhí)行
        while (true) {
            printThreadInfo();
            Thread.sleep(1000);
        }
    }

    /**
     * 輸出當(dāng)前線(xiàn)程的信息
     */
    private static void printThreadInfo() {
        System.out.println("當(dāng)前運(yùn)行的線(xiàn)程名為: " + Thread.currentThread().getName());
    }
}

運(yùn)行的結(jié)果如下:

當(dāng)前運(yùn)行的線(xiàn)程名為: main
當(dāng)前運(yùn)行的線(xiàn)程名為: Thread-1
當(dāng)前運(yùn)行的線(xiàn)程名為: Thread-0
當(dāng)前運(yùn)行的線(xiàn)程名為: main
當(dāng)前運(yùn)行的線(xiàn)程名為: Thread-1
當(dāng)前運(yùn)行的線(xiàn)程名為: Thread-0

第二種方式:實(shí)作Runnable介面

實(shí)作Runnable介面也是一種常見(jiàn)的建立執(zhí)行緒的方式,使用介面的方式可以讓我們的程式降低耦合度。 Runnable介面中僅僅定義了一個(gè)方法,就是run。我們來(lái)看看Runnable介面的程式碼。

package java.lang;

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

其實(shí)Runnable就是一個(gè)執(zhí)行緒任務(wù),執(zhí)行緒任務(wù)和執(zhí)行緒的控制分離,這也就是上面所說(shuō)的解耦。 我們要實(shí)作一個(gè)線(xiàn)程,可以藉助Thread類(lèi),Thread類(lèi)別要執(zhí)行的任務(wù)就可以由實(shí)作了Runnable介面的類(lèi)別來(lái)處理。 這就是Runnable的精髓之所在!

Runnable 是一個(gè)@FunctionalInterface 函數(shù)式接口,也就意味了可以利用JDK8提供的lambda的方式來(lái)創(chuàng)建線(xiàn)程任務(wù),後面的程式碼中會(huì)給讀者演示具體如何使用。

使用Runnable實(shí)作上面的範(fàn)例步驟如下:

  • 定義一個(gè)類(lèi)實(shí)現(xiàn)Runnable接口,作為線(xiàn)程任務(wù)類(lèi)
  • 重寫(xiě)run方法,并實(shí)現(xiàn)方法體,方法體的代碼就是線(xiàn)程所執(zhí)行的代碼
  • 定義一個(gè)可以運(yùn)行的類(lèi),并在main方法中創(chuàng)建線(xiàn)程任務(wù)類(lèi)
  • 創(chuàng)建Thread類(lèi),并將線(xiàn)程任務(wù)類(lèi)做為T(mén)hread類(lèi)的構(gòu)造方法傳入
  • 啟動(dòng)線(xiàn)程

1. 創(chuàng)建線(xiàn)程任務(wù)

線(xiàn)程任務(wù)就是線(xiàn)程要做的事情,這里我們讓這個(gè)線(xiàn)程每隔1s中打印自己的名字

package com.kingh.thread.create;

/**
 * 線(xiàn)程任務(wù)
 *
 * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a>
 * @version 1.0
 * @date 2019/3/18 10:04
 */
public class CreateThreadDemo4_Task implements Runnable {

    @Override
    public void run() {
		// 每隔1s中輸出一次當(dāng)前線(xiàn)程的名字
        while (true) {
            // 輸出線(xiàn)程的名字,與主線(xiàn)程名稱(chēng)相區(qū)分
            printThreadInfo();
            try {
                // 線(xiàn)程休眠一秒
                Thread.sleep(1000);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 輸出當(dāng)前線(xiàn)程的信息
     */
    private static void printThreadInfo() {
        System.out.println("當(dāng)前運(yùn)行的線(xiàn)程名為: " + Thread.currentThread().getName());
    }
}

2. 創(chuàng)建可運(yùn)行類(lèi)

在這里創(chuàng)建線(xiàn)程,并把任務(wù)交給線(xiàn)程處理,然后啟動(dòng)線(xiàn)程。

package com.kingh.thread.create;

/**
 * 創(chuàng)建線(xiàn)程
 *
 * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a>
 * @version 1.0
 * @date 2019/3/18 10:04
 */
public class CreateThreadDemo4_Main {

    public static void main(String[] args) throws Exception {
        // 實(shí)例化線(xiàn)程任務(wù)類(lèi)
        CreateThreadDemo4_Task task = new CreateThreadDemo4_Task();

        // 創(chuàng)建線(xiàn)程對(duì)象,并將線(xiàn)程任務(wù)類(lèi)作為構(gòu)造方法參數(shù)傳入
        new Thread(task).start();

        // 主線(xiàn)程的任務(wù),為了演示多個(gè)線(xiàn)程一起執(zhí)行
        while (true) {
            printThreadInfo();
            Thread.sleep(1000);
        }
    }

    /**
     * 輸出當(dāng)前線(xiàn)程的信息
     */
    private static void printThreadInfo() {
        System.out.println("當(dāng)前運(yùn)行的線(xiàn)程名為: " + Thread.currentThread().getName());
    }
}

線(xiàn)程任務(wù)和線(xiàn)程的控制分離,那么一個(gè)線(xiàn)程任務(wù)可以提交給多個(gè)線(xiàn)程來(lái)執(zhí)行。這是很有用的,比如車(chē)站的售票窗口,每個(gè)窗口可以看做是一個(gè)線(xiàn)程,他們每個(gè)窗口做的事情都是一樣的,也就是售票。這樣我們程序在模擬現(xiàn)實(shí)的時(shí)候就可以定義一個(gè)售票任務(wù),讓多個(gè)窗口同時(shí)執(zhí)行這一個(gè)任務(wù)。那么如果要改動(dòng)任務(wù)執(zhí)行計(jì)劃,只要修改線(xiàn)程任務(wù)類(lèi),所有的線(xiàn)程就都會(huì)按照修改后的來(lái)執(zhí)行。相比較繼承Thread類(lèi)的方式來(lái)創(chuàng)建線(xiàn)程的方式,實(shí)現(xiàn)Runnable接口是更為常用的。

3. lambda方式創(chuàng)建線(xiàn)程任務(wù)

這里就是為了簡(jiǎn)化內(nèi)部類(lèi)的編寫(xiě),簡(jiǎn)化了大量的模板代碼,顯得更加簡(jiǎn)潔。如果讀者看不明白,可以讀完內(nèi)部類(lèi)方式之后,回過(guò)來(lái)再看這段代碼。

package com.kingh.thread.create;

/**
 * 創(chuàng)建線(xiàn)程with lambda
 *
 * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a>
 * @version 1.0
 * @date 2019/3/18 10:04
 */
public class CreateThreadDemo5_Lambda {

    public static void main(String[] args) throws Exception {
        // 使用lambda的形式實(shí)例化線(xiàn)程任務(wù)類(lèi)
        Runnable task = () -> {
            while (true) {
                // 輸出線(xiàn)程的名字
                printThreadInfo();
            }
        };

        // 創(chuàng)建線(xiàn)程對(duì)象,并將線(xiàn)程任務(wù)類(lèi)作為構(gòu)造方法參數(shù)傳入
        new Thread(task).start();

        // 主線(xiàn)程的任務(wù),為了演示多個(gè)線(xiàn)程一起執(zhí)行
        while (true) {
            printThreadInfo();
            Thread.sleep(1000);
        }
    }

    /**
     * 輸出當(dāng)前線(xiàn)程的信息
     */
    private static void printThreadInfo() {
        System.out.println("當(dāng)前運(yùn)行的線(xiàn)程名為: " + Thread.currentThread().getName());
        try {
            Thread.sleep(1000);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

第三種方式:使用內(nèi)部類(lèi)的方式

這并不是一種新的實(shí)現(xiàn)線(xiàn)程的方式,只是另外的一種寫(xiě)法。比如有些情況我們的線(xiàn)程就想執(zhí)行一次,以后就用不到了。那么像上面兩種方式(繼承Thread類(lèi)和實(shí)現(xiàn)Runnable接口)都還要再定義一個(gè)類(lèi),顯得比較麻煩,我們就可以通過(guò)匿名內(nèi)部類(lèi)的方式來(lái)實(shí)現(xiàn)。使用內(nèi)部類(lèi)實(shí)現(xiàn)依然有兩種,分別是繼承Thread類(lèi)和實(shí)現(xiàn)Runnable接口。代碼如下:

package com.kingh.thread.create;

/**
 * 匿名內(nèi)部類(lèi)的方式創(chuàng)建線(xiàn)程
 *
 * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a>
 * @version 1.0
 * @date 2019/3/18 10:04
 */
public class CreateThreadDemo6_Anonymous {

    public static void main(String[] args) {
        // 基于子類(lèi)的方式
        new Thread() {
            @Override
            public void run() {
                while (true) {
                    printThreadInfo();
                }
            }
        }.start();

        // 基于接口的實(shí)現(xiàn)
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    printThreadInfo();
                }
            }
        }).start();
    }

    /**
     * 輸出當(dāng)前線(xiàn)程的信息
     */
    private static void printThreadInfo() {
        System.out.println("當(dāng)前運(yùn)行的線(xiàn)程名為: " + Thread.currentThread().getName());
        try {
            Thread.sleep(1000);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }
}

可以想象一下,我能不能既基于接口,又基于子類(lèi)呢?像下面的代碼會(huì)執(zhí)行出什么樣子呢?

package com.kingh.thread.create;

/**
 * 匿名內(nèi)部類(lèi)的方式創(chuàng)建線(xiàn)程
 *
 * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a>
 * @version 1.0
 * @date 2019/3/18 10:04
 */
public class CreateThreadDemo7_Anonymous {

    public static void main(String[] args) {
        // 基于子類(lèi)和接口的方式
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    printInfo("interface");
                }
            }
        }) {
            @Override
            public void run() {
                while (true) {
                    printInfo("sub class");
                }
            }
        }.start();
    }

    /**
     * 輸出當(dāng)前線(xiàn)程的信息
     */
    private static void printInfo(String text) {
        System.out.println(text);
        try {
            Thread.sleep(1000);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }
}

運(yùn)行結(jié)果如下:

sub class
sub class

我們可以看到,其實(shí)是基于子類(lèi)的執(zhí)行了,為什么呢,其實(shí)很簡(jiǎn)單,我們先來(lái)看一下為什么不基于子類(lèi)的時(shí)候Runnable的run方法可以執(zhí)行。這個(gè)要從Thread的源碼看起,下面是我截取的代碼片段。

public Thread(Runnable target)
    init(null, target, "Thread-" + nextThreadNum(), 0);
}

private void init(ThreadGroup g, Runnable target, String name,
                  long stackSize) {
    init(g, target, name, stackSize, null, true);
}

private void init(ThreadGroup g, Runnable target, String name,
                  long stackSize, AccessControlContext acc,
                  boolean inheritThreadLocals) {
    if (name == null) {
        throw new NullPointerException("name cannot be null");
    }

    this.name = name;

    Thread parent = currentThread();
    SecurityManager security = System.getSecurityManager();
    if (g == null) {
        /* Determine if it&#39;s an applet or not */

        /* If there is a security manager, ask the security manager
               what to do. */
        if (security != null) {
            g = security.getThreadGroup();
        }

        /* If the security doesn&#39;t have a strong opinion of the matter
               use the parent thread group. */
        if (g == null) {
            g = parent.getThreadGroup();
        }
    }

    /* checkAccess regardless of whether or not threadgroup is
           explicitly passed in. */
    g.checkAccess();

    /*
         * Do we have the required permissions?
         */
    if (security != null) {
        if (isCCLOverridden(getClass())) {
            security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
        }
    }

    g.addUnstarted();

    this.group = g;
    this.daemon = parent.isDaemon();
    this.priority = parent.getPriority();
    if (security == null || isCCLOverridden(parent.getClass()))
        this.contextClassLoader = parent.getContextClassLoader();
    else
        this.contextClassLoader = parent.contextClassLoader;
    this.inheritedAccessControlContext =
        acc != null ? acc : AccessController.getContext();
    this.target = target; // 注意這里
    setPriority(priority);
    if (inheritThreadLocals && parent.inheritableThreadLocals != null)
        this.inheritableThreadLocals =
        ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
    /* Stash the specified stack size in case the VM cares */
    this.stackSize = stackSize;

    /* Set thread ID */
    tid = nextThreadID();
}

其實(shí)上面的眾多代碼就是為了表現(xiàn) this.target = target 那么target是什么呢,是Thread類(lèi)的成員變量。那么在什么地方用到了target呢?下面是run方法的內(nèi)容。

@Override
public void run() {
    if (target != null) {
        target.run();
    }
}

我們可以看到,如果通過(guò)上面的構(gòu)造方法傳入target,那么就會(huì)執(zhí)行target中的run方法??赡苡信笥丫蜁?huì)問(wèn)了,我們同時(shí)繼承Thread類(lèi)和實(shí)現(xiàn)Runnable接口,target不為空,那么為何不執(zhí)行target的run呢。不要忘記了,我們?cè)谧宇?lèi)中已經(jīng)重寫(xiě)了Thread類(lèi)的run方法,因此run方法已經(jīng)不在是我們看到的這樣了。那當(dāng)然也就不回執(zhí)行target的run方法。

lambda 方式改造

剛才使用匿名內(nèi)部類(lèi),會(huì)發(fā)現(xiàn)代碼還是比較冗余的,lambda可以大大簡(jiǎn)化代碼的編寫(xiě)。用lambda來(lái)改寫(xiě)上面的基于接口的形式的代碼,如下

// 使用lambda的形式
new Thread(() -> {
    while (true) {
        printThreadInfo();
    }
}).start();


// 對(duì)比不使用lambda的形式
new Thread(new Runnable() {
    @Override
    public void run() {
        while (true) {
            printThreadInfo();
        }
    }
}).start();

第四種方式:定時(shí)器

定時(shí)器可以說(shuō)是一種基于線(xiàn)程的一個(gè)工具類(lèi),可以定時(shí)的來(lái)執(zhí)行某個(gè)任務(wù)。在應(yīng)用中經(jīng)常需要定期執(zhí)行一些操作,比如要在凌晨的時(shí)候匯總一些數(shù)據(jù),比如要每隔10分鐘抓取一次某個(gè)網(wǎng)站上的數(shù)據(jù)等等,總之計(jì)時(shí)器無(wú)處不在。

在Java中實(shí)現(xiàn)定時(shí)任務(wù)有很多種方式,JDK提供了Timer類(lèi)來(lái)幫助開(kāi)發(fā)者創(chuàng)建定時(shí)任務(wù),另外也有很多的第三方框架提供了對(duì)定時(shí)任務(wù)的支持,比如Spring的schedule以及著名的quartz等等。因?yàn)镾pring和quartz實(shí)現(xiàn)都比較重,依賴(lài)其他的包,上手稍微有些難度,不在本篇博客的討論范圍之內(nèi),這里就看一下JDK所給我們提供的API來(lái)實(shí)現(xiàn)定時(shí)任務(wù)。

1. 指定時(shí)間點(diǎn)執(zhí)行

package com.kingh.thread.create;

import java.text.SimpleDateFormat;
import java.util.Timer;
import java.util.TimerTask;

/**
 * 定時(shí)任務(wù)
 *
 * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a>
 * @version 1.0
 * @date 2019/3/18 10:04
 */
public class CreateThreadDemo9_Timer {

    private static final SimpleDateFormat format =
            new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");

    public static void main(String[] args) throws Exception {

        // 創(chuàng)建定時(shí)器
        Timer timer = new Timer();

        // 提交計(jì)劃任務(wù)
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("定時(shí)任務(wù)執(zhí)行了...");
            }
        }, format.parse("2017-10-11 22:00:00"));
    }
}

2.間隔時(shí)間重復(fù)執(zhí)行

package com.kingh.thread.create;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

/**
 * 定時(shí)任務(wù)
 *
 * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a>
 * @version 1.0
 * @date 2019/3/18 10:04
 */
public class CreateThreadDemo10_Timer {

    public static void main(String[] args){

        // 創(chuàng)建定時(shí)器
        Timer timer = new Timer();

        // 提交計(jì)劃任務(wù)
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("定時(shí)任務(wù)執(zhí)行了...");
            }
        },
                new Date(), 1000);
    }
}

關(guān)于Spring的定時(shí)任務(wù),可以參考 《Spring計(jì)劃任務(wù)》

第五種方式:帶返回值的線(xiàn)程實(shí)現(xiàn)方式

我們發(fā)現(xiàn)上面提到的不管是繼承Thread類(lèi)還是實(shí)現(xiàn)Runnable接口,發(fā)現(xiàn)有兩個(gè)問(wèn)題,第一個(gè)是無(wú)法拋出更多的異常,第二個(gè)是線(xiàn)程執(zhí)行完畢之后并無(wú)法獲得線(xiàn)程的返回值。那么下面的這種實(shí)現(xiàn)方式就可以完成我們的需求。這種方式的實(shí)現(xiàn)就是我們后面要詳細(xì)介紹的Future模式,只是在jdk5的時(shí)候,官方給我們提供了可用的API,我們可以直接使用。但是使用這種方式創(chuàng)建線(xiàn)程比上面兩種方式要復(fù)雜一些,步驟如下。

  • 創(chuàng)建一個(gè)類(lèi)實(shí)現(xiàn)Callable接口,實(shí)現(xiàn)call方法。這個(gè)接口類(lèi)似于Runnable接口,但比Runnable接口更加強(qiáng)大,增加了異常和返回值。

  • 創(chuàng)建一個(gè)FutureTask,指定Callable對(duì)象,做為線(xiàn)程任務(wù)。

  • 創(chuàng)建線(xiàn)程,指定線(xiàn)程任務(wù)。

  • 啟動(dòng)線(xiàn)程

代碼如下:

package com.kingh.thread.create;

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

/**
 * 帶返回值的方式
 *
 * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a>
 * @version 1.0
 * @date 2019/3/18 10:04
 */
public class CreateThreadDemo11_Callable {

    public static void main(String[] args) throws Exception {

        // 創(chuàng)建線(xiàn)程任務(wù)
        Callable<Integer> call = () -> {
            System.out.println("線(xiàn)程任務(wù)開(kāi)始執(zhí)行了....");
            Thread.sleep(2000);
            return 1;
        };

        // 將任務(wù)封裝為FutureTask
        FutureTask<Integer> task = new FutureTask<>(call);

        // 開(kāi)啟線(xiàn)程,執(zhí)行線(xiàn)程任務(wù)
        new Thread(task).start();

        // ====================
        // 這里是在線(xiàn)程啟動(dòng)之后,線(xiàn)程結(jié)果返回之前
        System.out.println("這里可以為所欲為....");
        // ====================

        // 為所欲為完畢之后,拿到線(xiàn)程的執(zhí)行結(jié)果
        Integer result = task.get();
        System.out.println("主線(xiàn)程中拿到異步任務(wù)執(zhí)行的結(jié)果為:" + result);
    }
}

執(zhí)行結(jié)果如下:

這里可以為所欲為....
線(xiàn)程任務(wù)開(kāi)始執(zhí)行了....
主線(xiàn)程中拿到異步任務(wù)執(zhí)行的結(jié)果為:1

Callable中可以通過(guò)范型參數(shù)來(lái)指定線(xiàn)程的返回值類(lèi)型。通過(guò)FutureTask的get方法拿到線(xiàn)程的返回值。

第六種方式:基于線(xiàn)程池的方式

我們知道,線(xiàn)程和數(shù)據(jù)庫(kù)連接這些資源都是非常寶貴的資源。那么每次需要的時(shí)候創(chuàng)建,不需要的時(shí)候銷(xiāo)毀,是非常浪費(fèi)資源的。那么我們就可以使用緩存的策略,也就是使用線(xiàn)程池。當(dāng)然了,線(xiàn)程池也不需要我們來(lái)實(shí)現(xiàn),jdk的官方也給我們提供了API。

代碼如下:

package com.kingh.thread.create;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 線(xiàn)程池
 *
 * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a>
 * @version 1.0
 * @date 2019/3/18 10:04
 */
public class CreateThreadDemo12_ThreadPool {

    public static void main(String[] args) throws Exception {

        // 創(chuàng)建固定大小的線(xiàn)程池
        ExecutorService threadPool = Executors.newFixedThreadPool(10);

        while (true) {
            // 提交多個(gè)線(xiàn)程任務(wù),并執(zhí)行
            threadPool.execute(new Runnable() {
                @Override
                public void run() {
                    printThreadInfo();
                }
            });
        }
    }

    /**
     * 輸出當(dāng)前線(xiàn)程的信息
     */
    private static void printThreadInfo() {
        System.out.println("當(dāng)前運(yùn)行的線(xiàn)程名為: " + Thread.currentThread().getName());
        try {
            Thread.sleep(1000);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }
}

執(zhí)行結(jié)果如下:

當(dāng)前運(yùn)行的線(xiàn)程名為: pool-1-thread-1
當(dāng)前運(yùn)行的線(xiàn)程名為: pool-1-thread-2
當(dāng)前運(yùn)行的線(xiàn)程名為: pool-1-thread-4
當(dāng)前運(yùn)行的線(xiàn)程名為: pool-1-thread-3
當(dāng)前運(yùn)行的線(xiàn)程名為: pool-1-thread-7
當(dāng)前運(yùn)行的線(xiàn)程名為: pool-1-thread-8
當(dāng)前運(yùn)行的線(xiàn)程名為: pool-1-thread-9
當(dāng)前運(yùn)行的線(xiàn)程名為: pool-1-thread-6
當(dāng)前運(yùn)行的線(xiàn)程名為: pool-1-thread-5
當(dāng)前運(yùn)行的線(xiàn)程名為: pool-1-thread-10

線(xiàn)程池的內(nèi)容還有非常多,這里不再詳細(xì)地講解。

更多編程相關(guān)知識(shí),請(qǐng)?jiān)L問(wèn):編程教學(xué)?。?/p>

以上是java實(shí)作多執(zhí)行緒的幾種方式的詳細(xì)內(nèi)容。更多資訊請(qǐng)關(guān)注PHP中文網(wǎng)其他相關(guān)文章!

本網(wǎng)站聲明
本文內(nèi)容由網(wǎng)友自願(yuàn)投稿,版權(quán)歸原作者所有。本站不承擔(dān)相應(yīng)的法律責(zé)任。如發(fā)現(xiàn)涉嫌抄襲或侵權(quán)的內(nèi)容,請(qǐng)聯(lián)絡(luò)admin@php.cn

熱AI工具

Undress AI Tool

Undress AI Tool

免費(fèi)脫衣圖片

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅(qū)動(dòng)的應(yīng)用程序,用於創(chuàng)建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線(xiàn)上人工智慧工具。

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費(fèi)的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費(fèi)的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強(qiáng)大的PHP整合開(kāi)發(fā)環(huán)境

Dreamweaver CS6

Dreamweaver CS6

視覺(jué)化網(wǎng)頁(yè)開(kāi)發(fā)工具

SublimeText3 Mac版

SublimeText3 Mac版

神級(jí)程式碼編輯軟體(SublimeText3)

熱門(mén)話(huà)題

Laravel 教程
1597
29
PHP教程
1488
72
VSCODE設(shè)置。 JSON位置 VSCODE設(shè)置。 JSON位置 Aug 01, 2025 am 06:12 AM

settings.json文件位於用戶(hù)級(jí)或工作區(qū)級(jí)路徑,用於自定義VSCode設(shè)置。 1.用戶(hù)級(jí)路徑:Windows為C:\Users\\AppData\Roaming\Code\User\settings.json,macOS為/Users//Library/ApplicationSupport/Code/User/settings.json,Linux為/home//.config/Code/User/settings.json;2.工作區(qū)級(jí)路徑:項(xiàng)目根目錄下的.vscode/settings

如何使用JDBC處理Java的交易? 如何使用JDBC處理Java的交易? Aug 02, 2025 pm 12:29 PM

要正確處理JDBC事務(wù),必須先關(guān)閉自動(dòng)提交模式,再執(zhí)行多個(gè)操作,最後根據(jù)結(jié)果提交或回滾;1.調(diào)用conn.setAutoCommit(false)以開(kāi)始事務(wù);2.執(zhí)行多個(gè)SQL操作,如INSERT和UPDATE;3.若所有操作成功則調(diào)用conn.commit(),若發(fā)生異常則調(diào)用conn.rollback()確保數(shù)據(jù)一致性;同時(shí)應(yīng)使用try-with-resources管理資源,妥善處理異常並關(guān)閉連接,避免連接洩漏;此外建議使用連接池、設(shè)置保存點(diǎn)實(shí)現(xiàn)部分回滾,並保持事務(wù)盡可能短以提升性能。

在Java的掌握依賴(lài)注入春季和Guice 在Java的掌握依賴(lài)注入春季和Guice Aug 01, 2025 am 05:53 AM

依賴(lài)性(di)IsadesignpatternwhereObjectsReceivedenciesenciesExtern上,推廣looseSecouplingAndEaseerTestingThroughConstructor,setter,orfieldInjection.2.springfraMefringframeWorkSannotationsLikeLikeLike@component@component,@component,@service,@autowiredwithjava-service和@autowiredwithjava-ligatiredwithjava-lase-lightike

Python Itertools組合示例 Python Itertools組合示例 Jul 31, 2025 am 09:53 AM

itertools.combinations用於生成從可迭代對(duì)像中選取指定數(shù)量元素的所有不重複組合(順序無(wú)關(guān)),其用法包括:1.從列表中選2個(gè)元素組合,如('A','B')、('A','C')等,避免重複順序;2.對(duì)字符串取3個(gè)字符組合,如"abc"、"abd",適用於子序列生成;3.求兩數(shù)之和等於目標(biāo)值的組合,如1 5=6,簡(jiǎn)化雙重循環(huán)邏輯;組合與排列的區(qū)別在於順序是否重要,combinations視AB與BA為相同,而permutations視為不同;

故障排除常見(jiàn)的java`ofmemoryError`場(chǎng)景'' 故障排除常見(jiàn)的java`ofmemoryError`場(chǎng)景'' Jul 31, 2025 am 09:07 AM

java.lang.OutOfMemoryError:Javaheapspace表示堆內(nèi)存不足,需檢查大對(duì)象處理、內(nèi)存洩漏及堆設(shè)置,通過(guò)堆轉(zhuǎn)儲(chǔ)分析工具定位並優(yōu)化代碼;2.Metaspace錯(cuò)誤因類(lèi)元數(shù)據(jù)過(guò)多,常見(jiàn)於動(dòng)態(tài)類(lèi)生成或熱部署,應(yīng)限制MaxMetaspaceSize並優(yōu)化類(lèi)加載;3.Unabletocreatenewnativethread因係統(tǒng)線(xiàn)程資源耗盡,需檢查線(xiàn)程數(shù)限制、使用線(xiàn)程池、調(diào)整棧大?。?.GCoverheadlimitexceeded指GC頻繁但回收少,應(yīng)分析GC日誌,優(yōu)化

Python Pytest夾具示例 Python Pytest夾具示例 Jul 31, 2025 am 09:35 AM

fixture是用於為測(cè)試提供預(yù)設(shè)環(huán)境或數(shù)據(jù)的函數(shù),1.使用@pytest.fixture裝飾器定義fixture;2.在測(cè)試函數(shù)中以參數(shù)形式註入fixture;3.yield之前執(zhí)行setup,之後執(zhí)行teardown;4.通過(guò)scope參數(shù)控製作用域,如function、module等;5.將共用fixture放在conftest.py中實(shí)現(xiàn)跨文件共享,從而提升測(cè)試的可維護(hù)性和復(fù)用性。

了解Java虛擬機(jī)(JVM)內(nèi)部 了解Java虛擬機(jī)(JVM)內(nèi)部 Aug 01, 2025 am 06:31 AM

TheJVMenablesJava’s"writeonce,runanywhere"capabilitybyexecutingbytecodethroughfourmaincomponents:1.TheClassLoaderSubsystemloads,links,andinitializes.classfilesusingbootstrap,extension,andapplicationclassloaders,ensuringsecureandlazyclassloa

如何使用Java的日曆? 如何使用Java的日曆? Aug 02, 2025 am 02:38 AM

使用java.time包中的類(lèi)替代舊的Date和Calendar類(lèi);2.通過(guò)LocalDate、LocalDateTime和LocalTime獲取當(dāng)前日期時(shí)間;3.使用of()方法創(chuàng)建特定日期時(shí)間;4.利用plus/minus方法不可變地增減時(shí)間;5.使用ZonedDateTime和ZoneId處理時(shí)區(qū);6.通過(guò)DateTimeFormatter格式化和解析日期字符串;7.必要時(shí)通過(guò)Instant與舊日期類(lèi)型兼容;現(xiàn)代Java中日期處理應(yīng)優(yōu)先使用java.timeAPI,它提供了清晰、不可變且線(xiàn)

See all articles