เป็นไปไม่ได้ที่จะสร้างพูลเธรดแบบแคชที่มีขนาด จำกัด ?


127

ดูเหมือนว่าจะเป็นไปไม่ได้ที่จะสร้างพูลเธรดที่แคชโดย จำกัด จำนวนเธรดที่สามารถสร้างได้

นี่คือวิธีการใช้งาน Executors.newCachedThreadPool แบบคงที่ในไลบรารี Java มาตรฐาน:

 public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

ดังนั้นการใช้เทมเพลตนั้นเพื่อสร้างพูลเธรดที่แคชขนาดคงที่:

new ThreadPoolExecutor(0, 3, 60L, TimeUnit.SECONDS, new SynchronusQueue<Runable>());

ตอนนี้ถ้าคุณใช้สิ่งนี้และส่งงาน 3 อย่างทุกอย่างจะเรียบร้อย การส่งงานเพิ่มเติมใด ๆ จะทำให้ถูกปฏิเสธข้อยกเว้นในการดำเนินการ

ลองทำสิ่งนี้:

new ThreadPoolExecutor(0, 3, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runable>());

จะส่งผลให้เธรดทั้งหมดดำเนินการตามลำดับ กล่าวคือเธรดพูลจะไม่สร้างเธรดมากกว่าหนึ่งเธรดเพื่อจัดการงานของคุณ

นี่คือบั๊กในวิธีการรันของ ThreadPoolExecutor? หรืออาจจะเป็นความตั้งใจ? หรือมีวิธีอื่นบ้าง?

แก้ไข: ฉันต้องการบางสิ่งที่เหมือนกับพูลเธรดที่แคชไว้ (มันสร้างเธรดตามความต้องการแล้วฆ่าพวกมันหลังจากหมดเวลา) แต่มีข้อ จำกัด เกี่ยวกับจำนวนเธรดที่สามารถสร้างได้และความสามารถในการจัดคิวงานเพิ่มเติมต่อเมื่อมี ถึงขีด จำกัด เธรด ตามการตอบสนองของ sjlee สิ่งนี้เป็นไปไม่ได้ ดูที่เมธอด execute () ของ ThreadPoolExecutor นั้นเป็นไปไม่ได้เลย ฉันจะต้อง subclass ThreadPoolExecutor และแทนที่ execute () เหมือนกับที่ SwingWorker ทำ แต่สิ่งที่ SwingWorker ทำใน execute () นั้นเป็นการแฮ็คที่สมบูรณ์


1
คำถามของคุณคืออะไร? ตัวอย่างรหัสที่ 2 ของคุณไม่ใช่คำตอบสำหรับชื่อของคุณหรือไม่?
rsp

4
ฉันต้องการเธรดพูลที่จะเพิ่มเธรดตามความต้องการเมื่อจำนวนงานเพิ่มขึ้น แต่จะไม่เพิ่มเธรดเกินจำนวนสูงสุด CachedThreadPool ทำสิ่งนี้อยู่แล้วยกเว้นว่าจะเพิ่มเธรดได้ไม่ จำกัด จำนวนและไม่หยุดที่ขนาดที่กำหนดไว้ล่วงหน้า ขนาดที่ฉันกำหนดในตัวอย่างคือ 3 ตัวอย่างที่สองเพิ่ม 1 เธรด แต่ไม่ได้เพิ่มอีกสองรายการเมื่อมีงานใหม่มาถึงในขณะที่งานอื่นยังไม่เสร็จสมบูรณ์
Matt Crinklaw-Vogt

ตรวจสอบสิ่งนี้มันแก้ไขได้debuggingisfun.blogspot.com/2012/05/…
ethan

เกี่ยวข้องกับ: stackoverflow.com/questions/19528304/…
Grey

คำตอบ:


235

ThreadPoolExecutor มีลักษณะการทำงานที่สำคัญหลายประการดังต่อไปนี้และปัญหาของคุณสามารถอธิบายได้ด้วยพฤติกรรมเหล่านี้

เมื่อมีการส่งงาน

  1. หากพูลเธรดมีขนาดไม่ถึงคอร์ก็จะสร้างเธรดใหม่
  2. หากถึงขนาดคอร์แล้วและไม่มีเธรดที่ไม่ได้ใช้งานระบบจะจัดคิวงาน
  3. หากถึงขนาดคอร์แล้วไม่มีเธรดที่ไม่ได้ใช้งานและคิวเต็มจะสร้างเธรดใหม่ (จนกว่าจะถึงขนาดสูงสุด)
  4. หากถึงขนาดสูงสุดแล้วจะไม่มีเธรดที่ไม่ได้ใช้งานและคิวจะเต็มนโยบายการปฏิเสธจะเริ่มทำงาน

ในตัวอย่างแรกโปรดทราบว่า SynchronousQueue มีขนาดเป็น 0 ดังนั้นเมื่อคุณถึงขนาดสูงสุด (3) นโยบายการปฏิเสธจะเริ่มต้นใน (# 4)

ในตัวอย่างที่สองคิวที่เลือกคือ LinkedBlockingQueue ซึ่งมีขนาดไม่ จำกัด ดังนั้นคุณจึงจมปลักกับพฤติกรรม # 2

คุณไม่สามารถปรับแต่งประเภทแคชหรือประเภทคงที่ได้มากนักเนื่องจากพฤติกรรมของพวกเขาถูกกำหนดไว้เกือบทั้งหมดแล้ว

หากคุณต้องการมีเธรดพูลที่มีขอบเขตและไดนามิกคุณต้องใช้ขนาดแกนบวกและขนาดสูงสุดรวมกับคิวที่มีขนาด จำกัด ตัวอย่างเช่น,

new ThreadPoolExecutor(10, // core size
    50, // max size
    10*60, // idle timeout
    TimeUnit.SECONDS,
    new ArrayBlockingQueue<Runnable>(20)); // queue with a size

ภาคผนวก : นี่เป็นคำตอบที่ค่อนข้างเก่าและดูเหมือนว่า JDK จะเปลี่ยนพฤติกรรมเมื่อพูดถึงขนาดคอร์เป็น 0 เนื่องจาก JDK 1.6 ถ้าขนาดคอร์เป็น 0 และพูลไม่มีเธรดใด ๆ ThreadPoolExecutor จะเพิ่ม a เธรดเพื่อดำเนินงานนั้น ดังนั้นขนาดคอร์ของ 0 จึงเป็นข้อยกเว้นของกฎข้างต้น ขอบคุณสตีฟสำหรับนำที่ให้ความสนใจของฉัน


4
คุณต้องเขียนคำไม่กี่คำเกี่ยวกับวิธีการallowCoreThreadTimeOutเพื่อให้คำตอบนี้สมบูรณ์แบบ ดูคำตอบของ @ user1046052
hsestupin

1
ตอบโจทย์มาก! ขอเพิ่มประเด็นเดียว: นโยบายการปฏิเสธอื่น ๆ ก็น่ากล่าวถึงเช่นกัน ดูคำตอบของ @brianegge
Jeff

1
พฤติกรรมที่ 2 ไม่ควรพูดว่า'ถ้าถึงขนาด maxThreadแล้วและไม่มีเธรดที่ไม่ได้ใช้งานระบบจะจัดคิวงาน' ?
Zoltán

1
ช่วยอธิบายรายละเอียดเกี่ยวกับขนาดของคิวได้หรือไม่? หมายความว่าสามารถจัดคิวงานได้เพียง 20 งานก่อนที่จะถูกปฏิเสธ?
Zoltán

1
@ Zoltánฉันเคยเขียนสิ่งนี้ไว้เมื่อไม่นานมานี้ดังนั้นจึงมีโอกาสที่พฤติกรรมบางอย่างอาจเปลี่ยนไปตั้งแต่นั้นมา (ฉันไม่ได้ติดตามกิจกรรมล่าสุดอย่างใกล้ชิดเกินไป) แต่สมมติว่าพฤติกรรมเหล่านี้ไม่เปลี่ยนแปลง # 2 ถูกต้องตามที่ระบุไว้และนั่นคือ อาจเป็นประเด็นที่สำคัญที่สุด (และค่อนข้างน่าแปลกใจ) ในเรื่องนี้ เมื่อถึงขนาดแกนแล้ว TPE จะสนับสนุนการสร้างเธรดใหม่ ขนาดคิวคือขนาดของคิวที่ส่งผ่านไปยัง TPE อย่างแท้จริง หากคิวเต็ม แต่ยังไม่ถึงขนาดสูงสุดคิวจะสร้างเธรดใหม่ (ไม่ปฏิเสธงาน) ดู # 3. หวังว่าจะช่วยได้
sjlee

60

ถ้าฉันพลาดอะไรไปวิธีแก้ปัญหาสำหรับคำถามเดิมนั้นง่ายมาก รหัสต่อไปนี้ใช้ในการทำงานที่ต้องการตามที่ผู้โพสต์ต้นฉบับอธิบายไว้ มันจะเกิดขึ้นสูงสุด 5 เธรดเพื่อทำงานบนคิวที่ไม่ถูกผูกมัดและเธรดที่ไม่ได้ใช้งานจะสิ้นสุดลงหลังจาก 60 วินาที

tp = new ThreadPoolExecutor(5, 5, 60, TimeUnit.SECONDS,
                    new LinkedBlockingQueue<Runnable>());
tp.allowCoreThreadTimeOut(true);

1
คุณถูก. วิธีนี้ถูกเพิ่มเข้ามาใน jdk 1.6 ดังนั้นจึงไม่ค่อยมีคนรู้เรื่องนี้มากนัก นอกจากนี้คุณไม่สามารถมีขนาดพูลแกน "ขั้นต่ำ" ซึ่งเป็นเรื่องที่น่าเสียดาย
jtahlborn

4
สิ่งเดียวที่ฉันกังวลเกี่ยวกับเรื่องนี้คือ (จากเอกสาร JDK 8): "เมื่อมีการส่งงานใหม่ใน method execute (Runnable) และมีเธรด corePoolSize น้อยกว่าที่รันอยู่เธรดใหม่จะถูกสร้างขึ้นเพื่อจัดการกับคำขอแม้ว่าผู้ปฏิบัติงานคนอื่น ๆ เธรดไม่ได้ใช้งาน "
veegee

ค่อนข้างแน่ใจว่านี่ใช้ไม่ได้จริง ครั้งสุดท้ายที่ฉันดูการทำข้างต้นจริง ๆ จะรันงานของคุณในเธรดเดียวเท่านั้นแม้ว่าคุณจะวางไข่ 5 ครั้งอีกครั้งเป็นเวลาไม่กี่ปี แต่เมื่อฉันใช้งาน ThreadPoolExecutor มันจะถูกส่งไปยังเธรดใหม่เมื่อคิวของคุณเต็มเท่านั้น การใช้คิวที่ไม่ถูกผูกไว้ทำให้สิ่งนี้ไม่เกิดขึ้น คุณสามารถทดสอบได้โดยส่งงานและ loggin'g ชื่อกระทู้จากนั้นนอน ทุก runnable จะพิมพ์ชื่อเดียวกัน / ไม่ถูกรันบนเธรดอื่น
Matt Crinklaw-Vogt

2
วิธีนี้ได้ผล Matt คุณตั้งค่าขนาดแกนเป็น 0 นั่นคือเหตุผลที่คุณมีเพียง 1 เธรด เคล็ดลับคือการกำหนดขนาดแกนเป็นขนาดสูงสุด
T-Gergely

1
@vegee ถูกต้อง - สิ่งนี้ไม่ได้ผลดีนัก - ThreadPoolExecutor จะใช้เธรดซ้ำเมื่ออยู่เหนือ corePoolSize เท่านั้น ดังนั้นเมื่อ corePoolSize เท่ากับ maxPoolSize คุณจะได้รับประโยชน์จากการแคชเธรดเมื่อพูลของคุณเต็มเท่านั้น (ดังนั้นหากคุณตั้งใจจะใช้สิ่งนี้ แต่โดยปกติจะอยู่ภายใต้ขนาดพูลสูงสุดของคุณคุณอาจลดระยะหมดเวลาเธรดให้เหลือน้อยที่สุด ค่า; และโปรดทราบว่าไม่มีการแคช - เธรดใหม่เสมอ)
Chris Riddell

7

มีปัญหาเดียวกัน เนื่องจากไม่มีคำตอบอื่นใดที่ทำให้ปัญหาทั้งหมดเข้าด้วยกันฉันจึงเพิ่มของฉัน:

ตอนนี้เขียนไว้อย่างชัดเจนในเอกสาร : หากคุณใช้คิวที่ไม่บล็อก ( LinkedBlockingQueue) การตั้งค่าเธรดสูงสุดจะไม่มีผลใด ๆ จะใช้เฉพาะเธรดคอร์เท่านั้น

ดังนั้น:

public class MyExecutor extends ThreadPoolExecutor {

    public MyExecutor() {
        super(4, 4, 5,TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
        allowCoreThreadTimeOut(true);
    }

    public void setThreads(int n){
        setMaximumPoolSize(Math.max(1, n));
        setCorePoolSize(Math.max(1, n));
    }

}

ตัวดำเนินการนี้มี:

  1. ไม่มีแนวคิดของเธรดสูงสุดเนื่องจากเราใช้คิวที่ไม่ถูกผูกไว้ นี่เป็นสิ่งที่ดีเพราะคิวดังกล่าวอาจทำให้ผู้ดำเนินการสร้างเธรดพิเศษที่ไม่ใช่คอร์จำนวนมากหากเป็นไปตามนโยบายปกติ

  2. Integer.MAX_VALUEคิวขนาดสูงสุด Submit()จะโยนถ้าจำนวนของงานที่ค้างอยู่เกินRejectedExecutionException Integer.MAX_VALUEไม่แน่ใจว่าหน่วยความจำเราจะหมดก่อนไม่งั้นจะเกิดขึ้น

  3. มี 4 เธรดหลักที่เป็นไปได้ เธรดหลักที่ไม่ได้ใช้งานจะออกโดยอัตโนมัติหากไม่มีการใช้งานเป็นเวลา 5 วินาทีดังนั้นใช่เธรดตามความต้องการอย่างเคร่งครัดจำนวนสามารถเปลี่ยนแปลงได้โดยใช้setThreads()วิธีการ

  4. ตรวจสอบให้แน่ใจว่าจำนวนเธรดคอร์ต่ำสุดต้องไม่น้อยกว่าหนึ่งเธรดมิฉะนั้นsubmit()จะปฏิเสธทุกงาน เนื่องจากเธรดหลักต้องเป็น> = เธรดสูงสุดเมธอดsetThreads()จึงตั้งค่าเธรดสูงสุดเช่นกันแม้ว่าการตั้งค่าเธรดสูงสุดจะไม่มีประโยชน์สำหรับคิวที่ไม่ถูกผูกไว้


ฉันคิดว่าคุณต้องตั้งค่า 'allowCoreThreadTimeOut' เป็น 'true' ด้วยมิฉะนั้นเมื่อสร้างเธรดแล้วคุณจะเก็บไว้ตลอดไป: gist.github.com/ericdcobb/46b817b384f5ca9d5f5d
eric

โอ๊ะฉันพลาดไปขออภัยคำตอบของคุณสมบูรณ์แบบแล้ว!
eric

6

ในตัวอย่างแรกของคุณ, งานที่ตามมาจะถูกปฏิเสธเพราะเป็นค่าเริ่มต้นAbortPolicy RejectedExecutionHandlerThreadPoolExecutor มีนโยบายต่อไปนี้ซึ่งคุณสามารถเปลี่ยนแปลงได้โดยใช้setRejectedExecutionHandlerวิธีการ:

CallerRunsPolicy
AbortPolicy
DiscardPolicy
DiscardOldestPolicy

ดูเหมือนว่าคุณต้องการพูลเธรดที่แคชด้วย CallerRunsPolicy


5

ไม่มีคำตอบใดที่แก้ไขปัญหาของฉันได้ซึ่งเกี่ยวข้องกับการสร้างการเชื่อมต่อ HTTP จำนวน จำกัด โดยใช้ไคลเอ็นต์ HTTP ของ Apache (เวอร์ชัน 3.x) เนื่องจากฉันใช้เวลาหลายชั่วโมงในการตั้งค่าที่ดีฉันจะแบ่งปัน:

private ExecutorService executor = new ThreadPoolExecutor(5, 10, 60L,
  TimeUnit.SECONDS, new SynchronousQueue<Runnable>(),
  Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());

สิ่งนี้จะสร้างThreadPoolExecutorซึ่งเริ่มต้นด้วยห้าและมีเธรดที่รันพร้อมกันได้สูงสุดสิบเธรดโดยใช้CallerRunsPolicyสำหรับการดำเนินการ


ปัญหาในการแก้ปัญหานี้คือถ้าคุณเพิ่มจำนวนหรือผู้ผลิตคุณจะเพิ่มจำนวนเธรดที่รันเธรดพื้นหลัง ในหลาย ๆ กรณีนั่นไม่ใช่สิ่งที่คุณต้องการ
Grey

3

ตาม Javadoc สำหรับ ThreadPoolExecutor:

หากมีมากกว่า corePoolSize แต่น้อยกว่าหัวข้อ maximumPoolSize ทำงานหัวข้อใหม่จะถูกสร้างขึ้นเฉพาะในกรณีที่คิวเต็ม โดยการตั้งค่า corePoolSize และ maximumPoolSize เดียวกันคุณจะสร้างเธรดพูลขนาดคงที่

(เน้นของฉัน)

คำตอบของความกระวนกระวายใจคือสิ่งที่คุณต้องการแม้ว่าฉันจะตอบคำถามอื่นของคุณก็ตาม :)


2

มีอีกหนึ่งทางเลือก แทนที่จะใช้ SynchronousQueue ใหม่คุณสามารถใช้คิวอื่น ๆ ได้เช่นกัน แต่คุณต้องแน่ใจว่าขนาดของมันคือ 1 เพื่อที่จะบังคับให้ผู้ดำเนินการบริการสร้างเธรดใหม่


ฉันคิดว่าคุณหมายถึงขนาด 0 (โดยค่าเริ่มต้น) ดังนั้นจะไม่มีงานเข้าคิวและบังคับให้บริการปฏิบัติการสร้างเธรดใหม่ทุกครั้ง
Leonmax

2

ดูไม่เหมือนกับว่าคำตอบใด ๆ ตอบคำถามได้จริง - ในความเป็นจริงฉันไม่เห็นวิธีการทำเช่นนี้แม้ว่าคุณจะคลาสย่อยจาก PooledExecutorService เนื่องจากวิธีการ / คุณสมบัติหลายอย่างเป็นแบบส่วนตัวเช่นการทำให้ addIfUnderMaximumPoolSize ได้รับการป้องกันคุณสามารถทำได้ ทำสิ่งต่อไปนี้:

class MyThreadPoolService extends ThreadPoolService {
    public void execute(Runnable run) {
        if (poolSize() == 0) {
            if (addIfUnderMaximumPoolSize(run) != null)
                return;
        }
        super.execute(run);
    }
}

สิ่งที่ใกล้เคียงที่สุดที่ฉันได้คือสิ่งนี้ - แต่ถึงอย่างนั้นก็ไม่ใช่วิธีแก้ปัญหาที่ดีนัก

new ThreadPoolExecutor(min, max, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>()) {
    public void execute(Runnable command) {
        if (getPoolSize() == 0 && getActiveCount() < getMaximumPoolSize()) {        
            super.setCorePoolSize(super.getCorePoolSize() + 1);
        }
        super.execute(command);
    }

    protected void afterExecute(Runnable r, Throwable t) {
         // nothing in the queue
         if (getQueue().isEmpty() && getPoolSize() > min) {
             setCorePoolSize(getCorePoolSize() - 1);
         }
    };
 };

ps ไม่ได้ทดสอบข้างต้น


2

นี่เป็นอีกวิธีหนึ่ง ฉันคิดว่าโซลูชันนี้ทำงานได้ตามที่คุณต้องการ (แม้ว่าจะไม่ภูมิใจกับโซลูชันนี้):

final LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>() {
    public boolean offer(Runnable o) {
        if (size() > 1)
            return false;
        return super.offer(o);
    };

    public boolean add(Runnable o) {
        if (super.offer(o))
            return true;
        else
            throw new IllegalStateException("Queue full");
    }
};

RejectedExecutionHandler handler = new RejectedExecutionHandler() {         
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        queue.add(r);
    }
};

dbThreadExecutor =
        new ThreadPoolExecutor(min, max, 60L, TimeUnit.SECONDS, queue, handler);

2

นี่คือสิ่งที่คุณต้องการ (อย่างน้อยฉันก็เดาอย่างนั้น) สำหรับคำอธิบายตรวจสอบคำตอบของ Jonathan Feinberg

Executors.newFixedThreadPool(int n)

สร้างเธรดพูลที่นำเธรดจำนวนคงที่กลับมาใช้ใหม่เพื่อดำเนินการกับคิวที่ไม่ถูกผูกไว้ที่แบ่งใช้ ณ จุดใดเธรด nThreads ส่วนใหญ่จะเป็นงานประมวลผลที่ใช้งานอยู่ หากงานเพิ่มเติมถูกส่งเมื่อเธรดทั้งหมดแอ็คทีฟงานจะรอในคิวจนกว่าเธรดจะพร้อมใช้งาน หากเธรดใด ๆ สิ้นสุดลงเนื่องจากความล้มเหลวในระหว่างการดำเนินการก่อนที่จะปิดระบบเธรดใหม่จะเข้ามาแทนที่หากจำเป็นเพื่อดำเนินการตามภารกิจในภายหลัง เธรดในพูลจะมีอยู่จนกว่าจะปิดอย่างชัดเจน


4
แน่นอนว่าฉันสามารถใช้พูลเธรดคงที่ได้ แต่จะปล่อยให้เธรดอยู่รอบ ๆ ตลอดไปหรือจนกว่าฉันจะเรียกการปิดระบบ ฉันต้องการบางสิ่งที่เหมือนกับพูลเธรดที่แคชไว้ (มันสร้างเธรดตามความต้องการแล้วฆ่าพวกมันหลังจากหมดเวลา) แต่มีข้อ จำกัด เกี่ยวกับจำนวนเธรดที่สามารถสร้างได้
Matt Crinklaw-Vogt

0
  1. คุณสามารถใช้ThreadPoolExecutorตามที่@sjlee แนะนำ

    คุณสามารถควบคุมขนาดของพูลแบบไดนามิก ดูคำถามนี้เพื่อดูรายละเอียดเพิ่มเติม:

    พูลเธรดแบบไดนามิก

    หรือ

  2. คุณสามารถใช้newWorkStealingPool API ซึ่งได้รับการแนะนำกับ java 8

    public static ExecutorService newWorkStealingPool()

    สร้างเธรดพูลที่ขโมยงานโดยใช้โปรเซสเซอร์ที่มีอยู่ทั้งหมดเป็นระดับความขนานของเป้าหมาย

โดยค่าเริ่มต้นระดับขนานจะถูกตั้งค่าเป็นจำนวนแกน CPU ในเซิร์ฟเวอร์ของคุณ หากคุณมีเซิร์ฟเวอร์ CPU 4 คอร์ขนาดเธรดพูลจะเป็น 4 API นี้จะส่งคืนForkJoinPoolประเภทExecutorService และอนุญาตให้ขโมยงานของเธรดที่ไม่ได้ใช้งานโดยขโมยงานจากเธรดที่ไม่ว่างใน ForkJoinPool


0

สรุปปัญหาได้ดังนี้

ฉันต้องการบางสิ่งที่เหมือนกับพูลเธรดที่แคชไว้ (มันสร้างเธรดตามความต้องการแล้วฆ่าพวกมันหลังจากหมดเวลา) แต่มีข้อ จำกัด เกี่ยวกับจำนวนเธรดที่สามารถสร้างได้และความสามารถในการจัดคิวงานเพิ่มเติมต่อไปเมื่อมันถึง ขีด จำกัด เธรด

ก่อนที่จะชี้ไปที่โซลูชันฉันจะอธิบายว่าทำไมวิธีแก้ปัญหาต่อไปนี้ไม่ได้ผล:

new ThreadPoolExecutor(0, 3, 60L, TimeUnit.SECONDS, new SynchronousQueue<>());

สิ่งนี้จะไม่จัดคิวงานใด ๆ เมื่อถึงขีด จำกัด 3 เนื่องจาก SynchronousQueue ตามนิยามไม่สามารถเก็บองค์ประกอบใด ๆ ได้

new ThreadPoolExecutor(0, 3, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>());

สิ่งนี้จะไม่สร้างมากกว่าเธรดเดียวเนื่องจาก ThreadPoolExecutor สร้างเฉพาะเธรดที่เกิน corePoolSize หากคิวเต็ม แต่ LinkedBlockingQueue จะไม่เต็ม

ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 3, 60, TimeUnit.SECONDS,
    new LinkedBlockingQueue<Runnable>());
executor.allowCoreThreadTimeOut(true);

สิ่งนี้จะไม่ใช้เธรดซ้ำจนกว่าจะถึง corePoolSize เนื่องจาก ThreadPoolExecutor เพิ่มจำนวนเธรดจนกว่าจะถึง corePoolSize แม้ว่าเธรดที่มีอยู่จะไม่ได้ใช้งานก็ตาม หากคุณสามารถอยู่กับข้อเสียนี้ได้นี่เป็นวิธีแก้ปัญหาที่ง่ายที่สุด นอกจากนี้ยังเป็นโซลูชันที่อธิบายไว้ใน "Java Concurrency in Practice" (เชิงอรรถบน p172)

ทางออกเดียวที่สมบูรณ์สำหรับปัญหาที่อธิบายไว้ดูเหมือนจะเป็นวิธีที่เกี่ยวข้องกับการแทนที่offerวิธีการของคิวและการเขียน a RejectedExecutionHandlerตามที่อธิบายไว้ในคำตอบของคำถามนี้: จะทำให้ ThreadPoolExecutor เพิ่มเธรดให้สูงสุดก่อนเข้าคิวได้อย่างไร


0

สิ่งนี้ใช้ได้กับ Java8 + (และอื่น ๆ สำหรับตอนนี้ .. )

     Executor executor = new ThreadPoolExecutor(3, 3, 5, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>()){{allowCoreThreadTimeOut(true);}};

โดยที่ 3 คือขีด จำกัด ของการนับเธรดและ 5 คือการหมดเวลาสำหรับเธรดที่ไม่ได้ใช้งาน

หากคุณต้องการตรวจสอบว่ามันใช้งานได้หรือไม่นี่คือรหัสในการทำงาน:

public static void main(String[] args) throws InterruptedException {
    final int DESIRED_NUMBER_OF_THREADS=3; // limit of number of Threads for the task at a time
    final int DESIRED_THREAD_IDLE_DEATH_TIMEOUT=5; //any idle Thread ends if it remains idle for X seconds

    System.out.println( java.lang.Thread.activeCount() + " threads");
    Executor executor = new ThreadPoolExecutor(DESIRED_NUMBER_OF_THREADS, DESIRED_NUMBER_OF_THREADS, DESIRED_THREAD_IDLE_DEATH_TIMEOUT, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>()) {{allowCoreThreadTimeOut(true);}};

    System.out.println(java.lang.Thread.activeCount() + " threads");

    for (int i = 0; i < 5; i++) {
        final int fi = i;
        executor.execute(() -> waitsout("starting hard thread computation " + fi, "hard thread computation done " + fi,2000));
    }
    System.out.println("If this is UP, it works");

    while (true) {
        System.out.println(
                java.lang.Thread.activeCount() + " threads");
        Thread.sleep(700);
    }

}

static void waitsout(String pre, String post, int timeout) {
    try {
        System.out.println(pre);
        Thread.sleep(timeout);
        System.out.println(post);
    } catch (Exception e) {
    }
}

ผลลัพธ์ของโค้ดด้านบนสำหรับฉันคือ

1 threads
1 threads
If this is UP, it works
starting hard thread computation 0
4 threads
starting hard thread computation 2
starting hard thread computation 1
4 threads
4 threads
hard thread computation done 2
hard thread computation done 0
hard thread computation done 1
starting hard thread computation 3
starting hard thread computation 4
4 threads
4 threads
4 threads
hard thread computation done 3
hard thread computation done 4
4 threads
4 threads
4 threads
4 threads
3 threads
3 threads
3 threads
1 threads
1 threads
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.