Pertanyaan Apa keuntungan dari Java-5 ThreadPoolExecutor melalui Java-7 ForkJoinPool?


Java 5 telah memperkenalkan dukungan untuk eksekusi tugas asynchronous oleh kolam thread dalam bentuk kerangka Executor, yang hatinya adalah kolam thread yang diimplementasikan oleh java.util.concurrent.ThreadPoolExecutor. Java 7 telah menambahkan alternatif kolam thread dalam bentuk java.util.concurrent.ForkJoinPool.

Melihat API masing-masing, ForkJoinPool menyediakan superset fungsionalitas ThreadPoolExecutor dalam skenario standar (meskipun secara tegas ThreadPoolExecutor menawarkan lebih banyak kesempatan untuk tuning daripada ForkJoinPool). Menambahkan ke pengamatan ini itu tugas fork / join tampaknya lebih cepat (mungkin karena pekerjaan mencuri scheduler), pasti membutuhkan lebih sedikit thread (karena operasi join non-blocking), orang mungkin mendapat kesan bahwa ThreadPoolExecutor telah digantikan oleh ForkJoinPool.

Tetapi apakah ini benar? Semua materi yang saya baca tampaknya menyimpulkan perbedaan yang agak samar antara dua jenis rangkaian thread:

  • ForkJoinPool adalah untuk banyak, tergantung, tugas yang dihasilkan, pendek, hampir tidak pernah memblokir (yaitu tugas-tugas intensif komputasi)
  • ThreadPoolExecutor adalah untuk beberapa, independen, eksternal yang dihasilkan, panjang, kadang-kadang memblokir tugas

Apakah perbedaan ini benar? Bisakah kita mengatakan sesuatu yang lebih spesifik tentang ini?


32
2018-02-14 12:23


asal


Jawaban:


ThreadPool (TP) dan ForkJoinPool (FJ) ditargetkan untuk berbagai kasus penggunaan. Perbedaan utama adalah dalam jumlah antrian yang digunakan oleh pelaksana yang berbeda yang memutuskan jenis masalah apa yang lebih cocok untuk pelaksana.

Pelaksana FJ memiliki n (alias tingkat paralelisme) antrian bersamaan yang terpisah (deques) sementara eksekutor TP hanya memiliki satu antrean bersamaan (antrean / deques ini mungkin implementasi kustom tidak mengikuti JDK Collections API). Akibatnya, dalam skenario di mana Anda memiliki sejumlah besar tugas (biasanya berjalan relatif singkat) yang dihasilkan, pelaksana FJ akan berkinerja lebih baik karena antrian independen akan meminimalkan operasi bersamaan dan mencuri yang jarang akan membantu dengan load balancing. Dalam TP karena antrian tunggal, akan ada operasi bersamaan setiap kali pekerjaan didekati dan itu akan bertindak sebagai bottleneck relatif dan membatasi kinerja.

Sebaliknya, jika ada relatif lebih sedikit tugas jangka panjang, antrean tunggal di TP tidak lagi menjadi penghambat kinerja. Namun, antrian n-independen dan upaya pencurian kerja yang relatif sering sekarang akan menjadi hambatan di FJ karena mungkin ada banyak usaha sia-sia untuk mencuri pekerjaan yang menambah overhead.

Selain itu, algoritma kerja-mencuri di FJ mengasumsikan bahwa tugas-tugas (yang lebih tua) dicuri dari deque akan menghasilkan tugas paralel yang cukup untuk mengurangi jumlah steal. Misalnya. di quicksort atau mergesort di mana tugas yang lebih lama menyamakan ke array yang lebih besar, tugas-tugas ini akan menghasilkan lebih banyak tugas dan menjaga antrian tidak kosong dan mengurangi jumlah mencuri secara keseluruhan. Jika ini tidak terjadi dalam aplikasi yang diberikan maka upaya mencuri sering lagi menjadi hambatan. Ini juga dicatat di javadoc untuk ForkJoinPool:

kelas ini menyediakan metode pemeriksaan status (misalnya getStealCount ())   yang dimaksudkan untuk membantu mengembangkan, menyetel, dan memantau   aplikasi fork / join.


14
2018-04-28 18:39



Bacaan yang Direkomendasikan http://gee.cs.oswego.edu/dl/jsr166/dist/docs/ Dari dokumen untuk ForkJoinPool:

ForkJoinPool berbeda dari jenis ExecutorService lainnya terutama oleh   keutamaan menggunakan mencuri pekerjaan: semua utas di kolam berusaha   temukan dan jalankan tugas yang diserahkan ke pool dan / atau dibuat oleh yang lain   tugas aktif (akhirnya memblokir menunggu pekerjaan jika tidak ada).   Ini memungkinkan pemrosesan yang efisien ketika sebagian besar tugas menelurkan subtugas lain   (seperti kebanyakan ForkJoinTasks), serta ketika banyak tugas kecil   dikirim ke kolam dari klien eksternal. Terutama saat pengaturan   asyncMode untuk true di konstruktor, ForkJoinPools mungkin juga   sesuai untuk digunakan dengan tugas-tugas gaya acara yang tidak pernah digabungkan.

Garpu bergabung kerangka kerja berguna untuk eksekusi paralel sementara layanan eksekutor memungkinkan untuk eksekusi bersamaan dan ada perbedaan. Lihat ini dan ini.

Garpu bergabung kerangka kerja juga memungkinkan untuk mencuri pekerjaan (penggunaan Deque).

Artikel ini adalah bacaan yang bagus.


10
2018-02-14 12:29



AFAIK, ForkJoinPool bekerja paling baik jika Anda memiliki karya besar dan Anda ingin itu dipecah secara otomatis. ThreadPoolExecutor adalah pilihan yang lebih baik jika Anda tahu bagaimana Anda ingin pekerjaannya terpecah. Untuk alasan ini saya cenderung menggunakan yang terakhir karena saya telah menentukan bagaimana saya ingin pekerjaan itu terpecah. Seperti itu bukan untuk setiap orang.

Tidak ada gunanya bahwa ketika datang ke potongan-potongan yang relatif acak dari logika bisnis, ThreadPoolExecutor akan melakukan semua yang Anda butuhkan, jadi mengapa membuatnya lebih rumit daripada yang Anda butuhkan.


2
2018-02-14 12:30



Mari kita bandingkan perbedaan konstruktor:

ThreadPoolExecutor

ThreadPoolExecutor(int corePoolSize, 
                   int maximumPoolSize, 
                   long keepAliveTime, 
                   TimeUnit unit, 
                   BlockingQueue<Runnable> workQueue, 
                   ThreadFactory threadFactory,
                   RejectedExecutionHandler handler)

ForkJoinPool

ForkJoinPool(int parallelism,
            ForkJoinPool.ForkJoinWorkerThreadFactory factory,
            Thread.UncaughtExceptionHandler handler,
            boolean asyncMode)

Satu-satunya keuntungan yang saya lihat ForkJoinPool: Bekerja mencuri mekanisme dengan utas iseng.

Java 8 telah memperkenalkan satu lagi API di Pelaku - newWorkStealingPool untuk membuat kolam kerja mencuri. Anda tidak harus membuat RecursiveTask dan RecursiveAction tetapi masih bisa digunakan ForkJoinPool.

public static ExecutorService newWorkStealingPool()

Menciptakan kolam thread kerja-mencuri menggunakan semua prosesor yang tersedia sebagai tingkat paralel targetnya.

Keuntungan dari ThreadPoolExecutor over ForkJoinPool:

  1. Anda dapat mengontrol ukuran antrean tugas dalam ThreadPoolExecutor tidak seperti di ForkJoinPool.
  2. Anda dapat menerapkan Kebijakan Penolakan saat Anda kehabisan kapasitas tidak seperti di ForkJoinPool

Saya suka dua fitur ini ThreadPoolExecutor yang menjaga kesehatan sistem dalam keadaan baik.

EDIT:

Lihatlah ini artikel untuk kasus-kasus penggunaan berbagai jenis kolam thread Executor Service dan evaluasi ForkJoin Pool fitur.


1
2018-01-18 13:19