国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

創(chuàng)建多線程的四種方式

這篇具有很好參考價(jià)值的文章主要介紹了創(chuàng)建多線程的四種方式。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

一、創(chuàng)建線程的四種方式

1. 繼承Thread類

① 創(chuàng)建一個(gè)類繼承Thread類,重寫run()方法
② 調(diào)用start()方法啟動(dòng)線程
例:

/* 創(chuàng)建三個(gè)窗口賣票,總票數(shù)為100 */
public class TicketWindow  extends Thread {

    //線程共享資源(100張票)
    private static int ticket = 100;
    //同步鎖必須是唯一的
    private static final Object obj = new Object();

    @Override
    public void run() {
        while(true){
            synchronized(obj){
                if(ticket > 0){

                    //暫停當(dāng)前線程,允許其它具有相同優(yōu)先級(jí)的線程獲得運(yùn)行機(jī)會(huì)
                    Thread.yield();

                    System.out.println(getName() + "賣票: 票號(hào):" + ticket);
                    ticket--;
                }else {
                    break;
                }
            }
        }
    }

    public static void main(String[] args) {
        TicketWindow w1 = new TicketWindow();
        w1.setName("一號(hào)窗口");
        w1.start();

        TicketWindow w2 = new TicketWindow();
        w2.setName("二號(hào)窗口");
        w2.start();

        Thread w3 = new TicketWindow(); //TicketWindow繼承了Thread,可以用Thread接收TicketWindow對(duì)象
        w3.setName("三號(hào)窗口");
        w3.start();
    }
}

2. 實(shí)現(xiàn)Runnable接口

① 創(chuàng)建類實(shí)現(xiàn)Runnable接口,重寫run()方法
② 以實(shí)現(xiàn)類作為構(gòu)造器參數(shù),創(chuàng)建一個(gè)線程(Thread)對(duì)象
③ 調(diào)用start()方法啟動(dòng)線程

/* 創(chuàng)建三個(gè)窗口賣票,總票數(shù)為100 */
public class TicketWindow  implements Runnable {

    //線程共享資源(100張票)
    private static int ticket = 100;
    //同步鎖必須是唯一的
    private static final Object obj = new Object();

    @Override
    public void run() {
        while(true){
            synchronized(obj){
                if(ticket > 0){
                    //暫停當(dāng)前線程,允許其它具有相同優(yōu)先級(jí)的線程執(zhí)行(不能保證運(yùn)行順序)
                    Thread.yield();
                    System.out.println(Thread.currentThread().getName() + "賣票: 票號(hào):" + ticket);
                    ticket--;
                }else {
                    break;
                }
            }
        }
    }

    public static void main(String[] args) {
        TicketWindow ticketWindow = new TicketWindow();

        new Thread(ticketWindow, "一號(hào)窗口").start();
        new Thread(ticketWindow, "二號(hào)窗口").start();
        new Thread(ticketWindow, "三號(hào)窗口").start();
    }
}

注意:實(shí)現(xiàn)Runnable接口方式中,調(diào)用的不是Thread類的run()方法,而是在線程啟動(dòng)后,去調(diào)用Runnable類型的run()方法,也就是我們傳入的實(shí)現(xiàn)類中的run()方法

3. 實(shí)現(xiàn)Callable接口

① 創(chuàng)建類實(shí)現(xiàn)Callable接口,重寫call()方法
② 創(chuàng)建實(shí)現(xiàn)類對(duì)象
③ 將實(shí)現(xiàn)類對(duì)象作為構(gòu)造器參數(shù),創(chuàng)建FutureTask對(duì)象
FutureTask對(duì)象作為構(gòu)造器參數(shù),創(chuàng)建Thread對(duì)象
⑤ 調(diào)用Thread對(duì)象的start()方法啟動(dòng)線程
⑥ 調(diào)用FutureTask對(duì)象的get()方法獲取返回值
例:

// 1. 創(chuàng)建一個(gè)類來實(shí)現(xiàn)Callable接口
public class TicketWindow  implements Callable<Object> {

    //線程共享資源(100張票)
    private static int ticket = 100;
    //同步鎖必須是唯一的
    private static final Object obj = new Object();

    // 2. 重寫call()方法,返回值類型可以根據(jù)需求指定,但必須與創(chuàng)建FutureTask對(duì)象時(shí)里面的泛型一致
    @Override
    public Object call() {
        while(true){
            synchronized(obj){
                if(ticket > 0){

                    //暫停當(dāng)前線程,允許其它具有相同優(yōu)先級(jí)的線程獲得運(yùn)行機(jī)會(huì)
                    Thread.yield();

                    System.out.println(Thread.currentThread().getName() + "賣票: 票號(hào):" + ticket);
                    ticket--;
                }else {
                    break;
                }
            }
        }
        return Thread.currentThread().getName() + "執(zhí)行完畢 ~ ~";
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 3. 創(chuàng)建Callable接口實(shí)現(xiàn)類的對(duì)象
        TicketWindow ticketWindow = new TicketWindow();
        
        // 4. 將實(shí)現(xiàn)類對(duì)象作為參數(shù)傳到FutureTask類的構(gòu)造器中,創(chuàng)建FutureTask類的對(duì)象
        FutureTask<Object> futureTask1 = new FutureTask<Object>(ticketWindow);
        // 5. 將FutureTask類對(duì)象傳到Thread類的構(gòu)造器中,創(chuàng)建線程對(duì)象(因?yàn)镕utureTask實(shí)現(xiàn)了Runnable接口,所以可以這樣傳)
        Thread t1 = new Thread(futureTask1, "1號(hào)窗口");
        // 6. 通過線程對(duì)象調(diào)用start()方法開啟線程
        t1.start();


        FutureTask<Object> futureTask2 = new FutureTask<Object>(ticketWindow);
        Thread t2 = new Thread(futureTask2, "2號(hào)窗口");
        t2.start();

		FutureTask<Object> futureTask3 = new FutureTask<Object>(ticketWindow);
        Thread t3 = new Thread(futureTask3, "3號(hào)窗口");
        t3.start();

        // 8. 調(diào)用FutureTask類的get()方法獲取call()方法的返回值,如果不需要返回值可以省略這一步
        Object result1= futureTask1.get();
        Object result2= futureTask2.get();
        Object result3= futureTask3.get();
        System.out.println(result);
    }
}

4. 線程池

使用線程池創(chuàng)建線程,是實(shí)際項(xiàng)目開發(fā)中最常用的方式,它擁有許多好處:

  • 提高響應(yīng)速度 (因?yàn)闇p少了創(chuàng)建新線程的時(shí)間)
  • 降低資源損耗 (重復(fù)利用線程池中的線程,不需要每次都創(chuàng)建和銷毀)
  • 便于線程的管理
import java.util.concurrent.*;

/* 水果售賣窗口 */
class FruitWindow implements Runnable {

    private int fruitNumber = 100;

    @Override
    public void run() {
        while (true) {
            /* 1.任何一個(gè)類的對(duì)象,都可以充當(dāng)鎖,一般使用Object類對(duì)象 */
            /* 2.多個(gè)線程必須共用一把鎖,多個(gè)線程搶一把鎖,誰搶到誰執(zhí)行 */
            synchronized (this) {
                if (fruitNumber > 0) {
                    try {
                        //休眠30毫秒
                        TimeUnit.MILLISECONDS.sleep(30);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    System.out.println(Thread.currentThread().getName() + "賣第:" + fruitNumber + "份水果");
                    fruitNumber--;

                } else {
                    break;
                }
            }
        }
    }
}

/* 蔬菜售賣窗口 */
class VegetableWindow implements Callable<Object> {

    private int vegetableNumber = 100;

    @Override
    public Object call() {
        while (true) {
            /* 1.任何一個(gè)類的對(duì)象,都可以充當(dāng)鎖,一般使用Object類對(duì)象 */
            /* 2.多個(gè)線程必須共用一把鎖,多個(gè)線程搶一把鎖,誰搶到誰執(zhí)行 */
            synchronized (this) {
                if (vegetableNumber > 0) {
                    try {
                        //休眠20毫秒
                        TimeUnit.MILLISECONDS.sleep(20);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    System.out.println(Thread.currentThread().getName() + "賣第:" + vegetableNumber + "份蔬菜");
                    vegetableNumber--;

                } else {
                    break;
                }
            }
        }
        return null;
    }
}


public class ThreadPool {

    public static void main(String[] args) {
        // 1.提供指定線程數(shù)量的線程池
        ExecutorService pool= Executors.newFixedThreadPool(10);

        // 設(shè)置線程池的屬性 (線程管理)
        ThreadPoolExecutor poolconfig = (ThreadPoolExecutor) pool; //設(shè)置屬性前,需要將接口類型 強(qiáng)轉(zhuǎn)為 實(shí)現(xiàn)類類型
        poolconfig .setCorePoolSize(10); //核心池的大?。ㄗ钚【€程數(shù))
        poolconfig .setMaximumPoolSize(15); //最大線程數(shù)
        //參數(shù)1:時(shí)間值。時(shí)間值為零將導(dǎo)致多余的線程在執(zhí)行任務(wù)后立即終止
        //參數(shù)2:時(shí)間單位,使用TimeUnit類指定,TimeUnit.DAYS為天、TimeUnit.HOURS為小時(shí)、TimeUnit.MINUTES為分鐘、TimeUnit.SECONDS為秒,TimeUnit.MICROSECONDS為微秒
        poolconfig .setKeepAliveTime(5, TimeUnit.MINUTES); //空閑線程存活時(shí)間
        // .  .  .

        // 2.執(zhí)行指定的線程操作 (需要提供實(shí)現(xiàn)了Runnable接口 或 Callable接口實(shí)現(xiàn)類的對(duì)象  )
        FruitWindow fruitWindow = new FruitWindow();
        /* 注意:操作共享數(shù)據(jù)的線程必須共用同一把鎖 */
        pool.execute(fruitWindow); //適用于Runnable
        pool.execute(fruitWindow); //適用于Runnable
        pool.execute(fruitWindow); //適用于Runnable

        VegetableWindow vegetableWindow = new VegetableWindow();
        /* 注意:操作共享數(shù)據(jù)的線程必須共用同一把鎖 */
        pool.submit(vegetableWindow); //適用于Callable
        pool.submit(vegetableWindow); //適用于Callable
        Future<Object> future = pool.submit(vegetableWindow); //適用于Callable

        // 獲取call()方法的返回值
        Object result = future.get();
        System.out.println(result);

        pool.shutdown(); //關(guān)閉連接池
    }
}

禁止使用 Executors 構(gòu)建線程池

上面的例子用到了Executors靜態(tài)工廠構(gòu)建線程池,但一般不建議這樣使用
Executors是一個(gè)Java中的工具類。提供工廠方法來創(chuàng)建不同類型的線程池。
創(chuàng)建多線程的四種方式
雖然它很大程度的簡化的創(chuàng)建線程池的過程,但是它有一個(gè)致命缺陷:Java開發(fā)手冊(cè)中提到,使用Executors創(chuàng)建線程池可能會(huì)導(dǎo)致OOM(Out-OfMemory , 內(nèi)存溢出 )

原因:
Executors的靜態(tài)方法創(chuàng)建線程池時(shí),用的是 LinkedBlockingQueue阻塞隊(duì)列,如創(chuàng)建固定線程池的方法newFixedThreadPool()

public static ExecutorService newFixedThreadPool(int nThreads) {
	return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
}

Java 中的 BlockingQueue 主要有兩種實(shí)現(xiàn),分別是 :

  • ArrayBlockingQueue
  • LinkedBlockingQueue。

ArrayBlockingQueue 是一個(gè)用數(shù)組實(shí)現(xiàn)的有界阻塞隊(duì)列,必須設(shè)置容量。
LinkedBlockingQueue 是一個(gè)用鏈表實(shí)現(xiàn)的有界阻塞隊(duì)列,在不設(shè)置的情況下,將是一個(gè)無邊界的阻塞隊(duì)列,最大長度為 Integer.MAX_VALUE。

這里的問題就出在這里,newFixedThreadPool 中創(chuàng)建 LinkedBlockingQueue 時(shí),并未指定容量。此時(shí),LinkedBlockingQueue 就是一個(gè)無邊界隊(duì)列,對(duì)于一個(gè)無邊界隊(duì)列來說,是可以不斷的向隊(duì)列中加入任務(wù)的,這種情況下就有可能因?yàn)槿蝿?wù)過多而導(dǎo)致內(nèi)存溢出問題。

上面提到的問題主要體現(xiàn)在 newFixedThreadPoolnewSingleThreadExecutor 兩個(gè)工廠方法上
newCachedThreadPoolnewScheduledThreadPool 這兩個(gè)方法雖然沒有用LinkedBlockingQueue阻塞隊(duì)列,但是它默認(rèn)的最大線程數(shù)是Integer.MAX_VALUE,也會(huì)有導(dǎo)致OOM的風(fēng)險(xiǎn)。

// 緩存線程池
public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
}
// 周期線程池
public ScheduledThreadPoolExecutor(int corePoolSize) {
   super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue());
}

構(gòu)建線程池的正確方式

避免使用 Executors 創(chuàng)建線程池,主要是避免使用其中的默認(rèn)實(shí)現(xiàn),那么我們可以自己直接調(diào)用 ThreadPoolExecutor 的構(gòu)造函數(shù)來自己創(chuàng)建線程池。在創(chuàng)建的同時(shí),給 BlockQueue 指定容量就可以了:

private static ExecutorService executor = new ThreadPoolExecutor(
												10,
												10,
												60L,
												TimeUnit.SECONDS,
												new ArrayBlockingQueue(10)
												);

還要指定拒絕策略,處理好任務(wù)隊(duì)列溢出時(shí)的異常問題。

參考資料:文章來源地址http://www.zghlxwxcb.cn/news/detail-496745.html

  1. CSDN CD4356 Java 多線程詳解(二):創(chuàng)建線程的4種方式
    https://blog.csdn.net/weixin_42950079/article/details/124862582
  2. Java開發(fā)手冊(cè)
    http://static.kancloud.cn/mtdev/java-manual/content/%E4%B8%BA%E4%BB%80%E4%B9%88%E7%A6%81%E6%AD%A2%E4%BD%BF%E7%94%A8Executors%E5%88%9B%E5%BB%BA%E7%BA%BF%E7%A8%8B%E6%B1%A0%EF%BC%9F.md

到了這里,關(guān)于創(chuàng)建多線程的四種方式的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 按照前序遍歷創(chuàng)建二叉樹及樹的四種遍歷方式

    按照前序遍歷創(chuàng)建二叉樹及樹的四種遍歷方式

    一.二叉樹的介紹 ????????二叉樹的特點(diǎn)是二叉樹的每個(gè)結(jié)點(diǎn)的度都不大于2,可以視為每個(gè)結(jié)點(diǎn)都有左孩子和右孩子。故二叉樹結(jié)點(diǎn)的數(shù)據(jù)結(jié)構(gòu)為 ? 二.二叉樹的特點(diǎn) ? ? 1.設(shè)根結(jié)點(diǎn)所在的層數(shù)為第1層,則第i層最多有個(gè)結(jié)點(diǎn)。 ? ? 2.深度為k的二叉樹最多有個(gè)結(jié)點(diǎn)。 ? ?

    2024年02月04日
    瀏覽(20)
  • Java多線程(1)---多線程認(rèn)識(shí)、四種創(chuàng)建方式以及線程狀態(tài)

    Java多線程(1)---多線程認(rèn)識(shí)、四種創(chuàng)建方式以及線程狀態(tài)

    目錄 前言 一.Java的多線程 1.1多線程的認(rèn)識(shí)? 1.2Java多線程的創(chuàng)建方式 1.3Java多線程的生命周期 1.4Java多線程的執(zhí)行機(jī)制 二.創(chuàng)建多線程的四種方式 2.1繼承Thread類 ?創(chuàng)建線程? ?Thread的構(gòu)造方法和常見屬性 ?2.2.實(shí)現(xiàn)Runnable接口 ?創(chuàng)建線程 ?使用lambda表達(dá)式創(chuàng)建 2.3實(shí)現(xiàn)Callable接口

    2024年02月14日
    瀏覽(23)
  • View 的四種 OnClick 方式

    View 的四種 OnClick 方式

    嗨嘍,大家好!今天呢,我跟大家聊一聊Android 的View 的點(diǎn)擊事件onClick 。額,有點(diǎn)拗口(^_^) 。 看過我的文章的人可能會(huì)好奇,你怎么寫Android的文章了啊?說起這啊,就是我的血淚史了,此處省略一萬字.................... 廢話不多說,讓我們代碼走起,風(fēng)里來,雨里去,唯有代

    2023年04月15日
    瀏覽(17)
  • STM32的四種開發(fā)方式

    STM32的四種開發(fā)方式

    首先看下ST官方給出的四種開發(fā)方式的比較 寄存器開發(fā) 寄存器編程對(duì)于從51等等芯片過渡過來的小伙伴并不陌生,不管你是什么庫,最終操作的還是寄存器,所以對(duì)于標(biāo)準(zhǔn)庫、HAL庫、LL庫都是在寄存器上的編程,所以可以直接在各種庫中直接操作寄存器。 但寄存器開發(fā)方法到

    2024年02月11日
    瀏覽(24)
  • CSS中的四種定位方式

    CSS中的四種定位方式

    在CSS中定位有以下4種: 靜態(tài)定位 - static 相對(duì)定位 - relative 絕對(duì)定位 - absolute 固定定位 - fixed 靜態(tài)定位是css中的默認(rèn)定位方式,也就是沒有定位。在此定位方式中設(shè)置:top,bottom,left,right,z-index 這些屬性都是無效的。 相對(duì)位置前的位置: 相對(duì)位置后的位置: 可以看到該

    2024年02月08日
    瀏覽(19)
  • JavaScript中的四種枚舉方式

    JavaScript中的四種枚舉方式

    字符串和數(shù)字具有無數(shù)個(gè)值,而其他類型如布爾值則是有限的集合。 一周的日子(星期一,星期二,...,星期日),一年的季節(jié)(冬季,春季,夏季,秋季)和基本方向(北,東,南,西)都是具有有限值集合的例子。 當(dāng)一個(gè)變量有一個(gè)來自有限的預(yù)定義常量的值時(shí),使用

    2024年02月03日
    瀏覽(24)
  • C#對(duì)象的四種比較方式

    1.ReferenceEquals(object?o1,?object?o2): 靜態(tài)方法: 比較兩個(gè)對(duì)象的引用,引用相同返回true,否則返回false,同為null是返回true; ReferenceEquals進(jìn)行值類型比較時(shí)總是返回false,因?yàn)閮蓚€(gè)值類型需要分別裝箱到對(duì)象中,是不同的引用?; 從名稱中便可知它用來比較兩者是否是相同的引

    2024年02月16日
    瀏覽(15)
  • SpringBoot導(dǎo)出Excel的四種方式

    SpringBoot導(dǎo)出Excel的四種方式

    ? ? ? ?近期接到了一個(gè)小需求,要將系統(tǒng)中的數(shù)據(jù)導(dǎo)出為Excel,且能將Excel數(shù)據(jù)導(dǎo)入到系統(tǒng)。對(duì)于大多數(shù)研發(fā)人員來說,這算是一個(gè)最基本的操作了。但是……我居然有點(diǎn)方! ? ? ? ? 好多年沒有實(shí)操這種基礎(chǔ)的功能了。我對(duì)于excel導(dǎo)入導(dǎo)出的印象還停留在才入行時(shí)的工作經(jīng)

    2024年02月03日
    瀏覽(25)
  • 記錄-實(shí)現(xiàn)深拷貝的四種方式

    記錄-實(shí)現(xiàn)深拷貝的四種方式

    深拷貝:在堆內(nèi)存中重新開辟一個(gè)存儲(chǔ)空間,完全克隆一個(gè)一模一樣的對(duì)象 淺拷貝:不在堆內(nèi)存中重新開辟空間,只復(fù)制棧內(nèi)存中的引用地址。本質(zhì)上兩個(gè)對(duì)象(數(shù)組)依然指向同一塊存儲(chǔ)空間 使用遞歸的方式進(jìn)行對(duì)象(數(shù)組)的深拷貝 奉上已封裝的深拷貝函數(shù)?? 上方函

    2023年04月21日
    瀏覽(21)
  • gRpc的四種通信方式詳細(xì)介紹

    gRpc的四種通信方式詳細(xì)介紹

    ???? 博主貓頭虎 帶您 Go to New World.??? ?? 博客首頁——貓頭虎的博客?? ??《面試題大全專欄》 文章圖文并茂??生動(dòng)形象??簡單易學(xué)!歡迎大家來踩踩~?? ?? 《IDEA開發(fā)秘籍專欄》學(xué)會(huì)IDEA常用操作,工作效率翻倍~?? ?? 《100天精通Golang(基礎(chǔ)入門篇)》學(xué)會(huì)Golang語言

    2024年02月11日
    瀏覽(19)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包