最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java如何自定義線程池中隊(duì)列

 更新時(shí)間:2022年07月07日 11:40:40   作者:??你呀不牛??  
這篇文章主要介紹了Java如何自定義線程池中隊(duì)列,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下

背景

業(yè)務(wù)交互的過程中涉及到了很多關(guān)于SFTP下載的問題,因此在代碼中定義了一些線程池,使用中發(fā)現(xiàn)了一些問題,

代碼類似如下所示:

public class ExecutorTest {
    private static ExecutorService es = new ThreadPoolExecutor(2,
            100, 1000, TimeUnit.MILLISECONDS
            , new ArrayBlockingQueue<>(10));
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            es.submit(new MyThread());
        }
    }
    static class MyThread implements Runnable {
        @Override
        public void run() {
            for (; ; ) {
                System.out.println("Thread name=" + Thread.currentThread().getName());
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

如上面的代碼所示,定義了一個(gè)初始容量為2,最大容量為100,隊(duì)列長度為10的線程池,期待的運(yùn)行結(jié)果為:

Thread name=pool-1-thread-1
Thread name=pool-1-thread-2
Thread name=pool-1-thread-3
Thread name=pool-1-thread-4
Thread name=pool-1-thread-5
Thread name=pool-1-thread-6
Thread name=pool-1-thread-7
Thread name=pool-1-thread-8
Thread name=pool-1-thread-9
Thread name=pool-1-thread-10
Thread name=pool-1-thread-3
Thread name=pool-1-thread-5
Thread name=pool-1-thread-2
Thread name=pool-1-thread-1
Thread name=pool-1-thread-4
Thread name=pool-1-thread-10
Thread name=pool-1-thread-7
Thread name=pool-1-thread-6
Thread name=pool-1-thread-9
Thread name=pool-1-thread-8
Thread name=pool-1-thread-3
Thread name=pool-1-thread-4
Thread name=pool-1-thread-1
Thread name=pool-1-thread-5
Thread name=pool-1-thread-2
Thread name=pool-1-thread-8
Thread name=pool-1-thread-6
Thread name=pool-1-thread-7
Thread name=pool-1-thread-9
Thread name=pool-1-thread-10

期待十個(gè)線程都可以運(yùn)行,但實(shí)際的執(zhí)行效果如下:

Thread name=pool-1-thread-1
Thread name=pool-1-thread-2
Thread name=pool-1-thread-2
Thread name=pool-1-thread-1
Thread name=pool-1-thread-1
Thread name=pool-1-thread-2
Thread name=pool-1-thread-1
Thread name=pool-1-thread-2
Thread name=pool-1-thread-2
Thread name=pool-1-thread-1
Thread name=pool-1-thread-2
Thread name=pool-1-thread-1
Thread name=pool-1-thread-2
Thread name=pool-1-thread-1

對(duì)比可以看出,用上面的方式定義線程池,最終只有兩個(gè)線程可以運(yùn)行,即線程池的初始容量大小。其余線程都被阻塞到了隊(duì)列ArrayBlockingQueue<>(10)

問題分析

我們知道,Executors框架提供了幾種常見的線程池分別為:

  • newCachedThreadPool創(chuàng)建一個(gè)可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閑線程,若無可回收,則新建線程。
  • newFixedThreadPool 創(chuàng)建一個(gè)定長線程池,可控制線程最大并發(fā)數(shù),超出的線程會(huì)在隊(duì)列中等待。
  • newScheduledThreadPool 創(chuàng)建一個(gè)定長線程池,支持定時(shí)及周期性任務(wù)執(zhí)行。
  • newSingleThreadExecutor 創(chuàng)建一個(gè)單線程化的線程池,它只會(huì)用唯一的工作線程來執(zhí)行任務(wù),保證所有任務(wù)按照指定順序(FIFO, LIFO, 優(yōu)先級(jí))執(zhí)行。

如果將代碼中自定義的線程池改為 :

private static ExecutorService es = Executors.newCachedThreadPool();

運(yùn)行發(fā)現(xiàn),提交的十個(gè)線程都可以運(yùn)行

Executors.newCachedThreadPool()的源碼如下:

/**
 * Creates a thread pool that creates new threads as needed, but
 * will reuse previously constructed threads when they are
 * available.  These pools will typically improve the performance
 * of programs that execute many short-lived asynchronous tasks.
 * Calls to {@code execute} will reuse previously constructed
 * threads if available. If no existing thread is available, a new
 * thread will be created and added to the pool. Threads that have
 * not been used for sixty seconds are terminated and removed from
 * the cache. Thus, a pool that remains idle for long enough will
 * not consume any resources. Note that pools with similar
 * properties but different details (for example, timeout parameters)
 * may be created using {@link ThreadPoolExecutor} constructors.
 *
 * @return the newly created thread pool
 */
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

通過對(duì)比發(fā)現(xiàn),newCachedThreadPool使用的是 SynchronousQueue<>()而我們使用的是ArrayBlockingQueue<>(10) 因此可以很容易的發(fā)現(xiàn)問題出在隊(duì)列上。

問題解決

將ArrayBlockingQueue改為SynchronousQueue 問題解決,代碼如下:

public class ExecutorTest {
    private static ExecutorService es = new ThreadPoolExecutor(2,
            100, 1000, TimeUnit.MILLISECONDS
            , new SynchronousQueue<>());
    private static ExecutorService es2 = Executors.newCachedThreadPool();
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            es.submit(new MyThread());
        }
    }
    static class MyThread implements Runnable {
        @Override
        public void run() {
            for (; ; ) {
                System.out.println("Thread name=" + Thread.currentThread().getName());
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

總結(jié)

兩個(gè)隊(duì)列的UML關(guān)系圖

從圖上我們可以看到,兩個(gè)隊(duì)列都繼承了AbstractQueue實(shí)現(xiàn)了BlockingQueue接口,因此功能應(yīng)該相似

SynchronousQueue的定義

* <p>Synchronous queues are similar to rendezvous channels used in
* CSP and Ada. They are well suited for handoff designs, in which an
* object running in one thread must sync up with an object running
* in another thread in order to hand it some information, event, or
* task.

SynchronousQueue類似于一個(gè)傳遞通道,只是通過他傳遞某個(gè)元素,并沒有任何容量,只有當(dāng)?shù)谝粋€(gè)元素被取走,才能在給隊(duì)列添加元素。

ArrayBlockingQueue的定義

* A bounded {@linkplain BlockingQueue blocking queue} backed by an
* array.  This queue orders elements FIFO (first-in-first-out).  The
* <em>head</em> of the queue is that element that has been on the
* queue the longest time.  The <em>tail</em> of the queue is that
* element that has been on the queue the shortest time. New elements
* are inserted at the tail of the queue, and the queue retrieval
* operations obtain elements at the head of the queue.

ArrayBlockingQueue從定義來看就是一個(gè)普通的隊(duì)列,先入先出,當(dāng)隊(duì)列為空時(shí),獲取數(shù)據(jù)的線程會(huì)被阻塞,當(dāng)隊(duì)列滿時(shí),添加隊(duì)列的線程會(huì)被阻塞,直到隊(duì)列可用。

分析

從上面隊(duì)列的定義中可以看出,導(dǎo)致線程池沒有按照預(yù)期運(yùn)行的原因不是因?yàn)殛?duì)列的問題,應(yīng)該是關(guān)于線程池在提交任務(wù)時(shí),從隊(duì)列取數(shù)據(jù)的方式不同導(dǎo)致的。

jdk源碼中關(guān)于線程池隊(duì)列的說明

* <dt>Queuing</dt>
*
* <dd>Any {@link BlockingQueue} may be used to transfer and hold
* submitted tasks.  The use of this queue interacts with pool sizing:
*
* <ul>
*
* <li> If fewer than corePoolSize threads are running, the Executor
* always prefers adding a new thread
* rather than queuing.</li>
*
* <li> If corePoolSize or more threads are running, the Executor
* always prefers queuing a request rather than adding a new
* thread.</li>
*
* <li> If a request cannot be queued, a new thread is created unless
* this would exceed maximumPoolSize, in which case, the task will be
* rejected.</li>

從說明中可以看到,如果正在運(yùn)行的線程數(shù)必初始容量corePoolSize小,那么Executor會(huì)從創(chuàng)建一個(gè)新線程去執(zhí)行任務(wù),如果正在執(zhí)行的線程數(shù)必corePoolSize大,那么Executor會(huì)將新提交的任務(wù)放到阻塞隊(duì)列,除非當(dāng)隊(duì)列的個(gè)數(shù)超過了隊(duì)列的最大長度maxmiumPooSize。

從源碼中找到關(guān)于提交任務(wù)的方法:

public Future<?> submit(Runnable task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<Void> ftask = newTaskFor(task, null);
    execute(ftask);
    return ftask;
}

從源碼中看到 subimit實(shí)際上是調(diào)用了execute方法

execute方法的源碼:

public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    /*
     * Proceed in 3 steps:
     *
     * 1. If fewer than corePoolSize threads are running, try to
     * start a new thread with the given command as its first
     * task.  The call to addWorker atomically checks runState and
     * workerCount, and so prevents false alarms that would add
     * threads when it shouldn't, by returning false.
     *
     * 2. If a task can be successfully queued, then we still need
     * to double-check whether we should have added a thread
     * (because existing ones died since last checking) or that
     * the pool shut down since entry into this method. So we
     * recheck state and if necessary roll back the enqueuing if
     * stopped, or start a new thread if there are none.
     *
     * 3. If we cannot queue task, then we try to add a new
     * thread.  If it fails, we know we are shut down or saturated
     * and so reject the task.
     */
    int c = ctl.get();
    if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
        if (! isRunning(recheck) && remove(command))
            reject(command);
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    else if (!addWorker(command, false))
        reject(command);
}

源碼中可以看出,提交任務(wù)時(shí),首先會(huì)判斷正在執(zhí)行的線程數(shù)是否小于corePoolSize,如果條件成立那么會(huì)直接創(chuàng)建線程并執(zhí)行任務(wù)。如果條件不成立,且隊(duì)列沒有滿,那么將任務(wù)放到隊(duì)列,如果條件不成立但是隊(duì)列滿了,那么同樣也新創(chuàng)建線程并執(zhí)行任務(wù)。

到此這篇關(guān)于Java如何自定義線程池中隊(duì)列的文章就介紹到這了,更多相關(guān)Java 隊(duì)列內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • JVM垃圾收集器詳解

    JVM垃圾收集器詳解

    本文主要介紹了JVM垃圾收集器的相關(guān)知識(shí)。具有很好的參考價(jià)值,下面跟著小編一起來看下吧
    2017-02-02
  • Java實(shí)現(xiàn)帶緩沖的輸入輸出流

    Java實(shí)現(xiàn)帶緩沖的輸入輸出流

    本文詳細(xì)講解了Java實(shí)現(xiàn)帶緩沖的輸入輸出流,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-04-04
  • Java 線程池詳解及創(chuàng)建簡單實(shí)例

    Java 線程池詳解及創(chuàng)建簡單實(shí)例

    這篇文章主要介紹了Java 線程池詳解及創(chuàng)建簡單實(shí)例的相關(guān)資料,需要的朋友可以參考下
    2017-02-02
  • JavaWeb文件上傳與下載功能解析

    JavaWeb文件上傳與下載功能解析

    這篇文章主要為大家詳細(xì)介紹了JavaWeb文件上傳與下載功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-06-06
  • java ArrayList按照同一屬性進(jìn)行分組

    java ArrayList按照同一屬性進(jìn)行分組

    這篇文章主要介紹了java ArrayList按照同一屬性進(jìn)行分組的相關(guān)資料,需要的朋友可以參考下
    2017-02-02
  • Java正則驗(yàn)證IP的方法實(shí)例分析【測試可用】

    Java正則驗(yàn)證IP的方法實(shí)例分析【測試可用】

    這篇文章主要介紹了Java正則驗(yàn)證IP的方法,結(jié)合實(shí)例形式對(duì)比分析了網(wǎng)上常見的幾種針對(duì)IP的正則驗(yàn)證方法,最終給出了一個(gè)比較靠譜的IP正則驗(yàn)證表達(dá)式,需要的朋友可以參考下
    2017-08-08
  • JavaWeb中過濾器Filter的用法詳解

    JavaWeb中過濾器Filter的用法詳解

    過濾器通常對(duì)一些web資源進(jìn)行攔截,做完一些處理器再交給下一個(gè)過濾器處理,直到所有的過濾器處理器,再調(diào)用servlet實(shí)例的service方法進(jìn)行處理。本文將通過示例為大家講解JavaWeb中過濾器Filter的用法與實(shí)現(xiàn),需要的可以參考一下
    2022-08-08
  • Java集合中的Iterator迭代器接口詳解

    Java集合中的Iterator迭代器接口詳解

    這篇文章主要介紹了Java集合中的Iterator迭代器接口詳解,Iterator,被稱為迭代器接口,本身并不提供存儲(chǔ)對(duì)象的能力,主要用于遍歷Collection中的元素,需要的朋友可以參考下
    2023-11-11
  • Java線程中start和run方法全面解析

    Java線程中start和run方法全面解析

    這篇文章主要介紹了Java線程中start和run方法的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2016-08-08
  • Ajax 驗(yàn)證用戶輸入的驗(yàn)證碼是否與隨機(jī)生成的一致

    Ajax 驗(yàn)證用戶輸入的驗(yàn)證碼是否與隨機(jī)生成的一致

    本文主要介紹了后臺(tái)驗(yàn)證用戶輸入的驗(yàn)證碼是否與隨機(jī)生成的驗(yàn)證碼一致的方法,具有很好的參考價(jià)值,下面跟著小編一起來看下吧
    2017-02-02

最新評(píng)論

措勤县| 八宿县| 沙河市| 卢湾区| 泽库县| 宿州市| 焦作市| 惠水县| 奉贤区| 都昌县| 古田县| 大悟县| 门头沟区| 汉阴县| 长治县| 邵武市| 日照市| 武陟县| 贡山| 出国| 井陉县| 遵义市| 长武县| 大荔县| 巴中市| 遵义市| 深州市| 红桥区| 元朗区| 应用必备| 民和| 涞源县| 张家港市| 上虞市| 同心县| 新建县| 翼城县| 德兴市| 军事| 扬中市| 松江区|