Satu lagi jenis kumpulan benang ialah "dicache". Kumpulan benang sebegini sama biasa digunakan seperti yang tetap.

Seperti yang ditunjukkan oleh namanya, kumpulan benang jenis ini menyimpan cache benang. Ia mengekalkan benang yang tidak digunakan hidup untuk masa yang terhad untuk menggunakan semula benang tersebut untuk melaksanakan tugas baharu. Kumpulan benang sedemikian adalah yang terbaik apabila kita mempunyai jumlah kerja ringan yang munasabah.

Maksud "jumlah yang munasabah" agak luas, tetapi anda harus tahu bahawa kumpulan sedemikian tidak sesuai untuk setiap bilangan tugas. Sebagai contoh, katakan kita ingin mencipta sejuta tugas. Walaupun setiap satu mengambil masa yang sangat kecil, kami masih akan menggunakan jumlah sumber yang tidak munasabah dan merendahkan prestasi. Kita juga harus mengelakkan kumpulan sedemikian apabila masa pelaksanaan tidak dapat diramalkan, contohnya, dengan tugas I/O.

Di bawah tudung, pembina ThreadPoolExecutor dipanggil dengan hujah berikut:

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

Nilai berikut dihantar kepada pembina sebagai argumen:

Parameter Nilai
corePoolSize (berapa banyak utas yang akan siap (bermula) apabila perkhidmatan pelaksana bermula) 0
maximumPoolSize (bilangan maksimum utas yang boleh dibuat oleh perkhidmatan pelaksana ) Integer.MAX_VALUE
keepAliveTime (masa benang yang dibebaskan akan terus hidup sebelum dimusnahkan jika bilangan utas lebih besar daripada corePoolSize ) 60L
unit (unit masa) TimeUnit.SECONDS
workQueue (pelaksanaan baris gilir) SynchronousQueue<Runnable>() baharu

Dan kita boleh lulus pelaksanaan ThreadFactory kita sendiri dengan cara yang sama.

Mari bercakap tentang SynchronousQueue

Idea asas pemindahan segerak adalah agak mudah tetapi berlawanan intuitif (iaitu, gerak hati atau akal fikiran memberitahu anda bahawa ia adalah salah): anda boleh menambah elemen pada baris gilir jika dan hanya jika utas lain menerima elemen di masa yang sama. Dalam erti kata lain, baris gilir segerak tidak boleh mempunyai tugas di dalamnya, kerana sebaik sahaja tugasan baharu tiba, urutan pelaksana telah pun mengambil tugas itu .

Apabila tugasan baharu memasuki baris gilir, jika terdapat utas aktif percuma dalam kolam, maka tugasan itu akan diambil. Jika semua utas sibuk, maka utas baharu akan dibuat.

Kolam tembolok bermula dengan sifar urutan dan berpotensi berkembang menjadi Integer.MAX_VALUE urutan. Pada asasnya, saiz kumpulan benang cache dihadkan hanya oleh sumber sistem.

Untuk menjimatkan sumber sistem, kumpulan benang cache mengalih keluar benang yang melahu selama satu minit.

Mari lihat bagaimana ia berfungsi dalam amalan. Kami akan membuat kelas tugas yang memodelkan permintaan pengguna:

public class Task implements Runnable {
   int taskNumber;

   public Task(int taskNumber) {
       this.taskNumber = taskNumber;
   }

   @Override
   public void run() {
       System.out.println("Processed user request #" + taskNumber + " on thread " + Thread.currentThread().getName());
   }
}

Dalam kaedah utama , kami mencipta newCachedThreadPool dan kemudian menambah 3 tugasan untuk pelaksanaan. Di sini kami mencetak status perkhidmatan kami (1) .

Seterusnya, kami berhenti seketika selama 30 saat, memulakan tugasan lain dan memaparkan status (2) .

Selepas itu, kami menjeda urutan utama kami selama 70 saat, mencetak status (3) , kemudian sekali lagi menambah 3 tugasan, dan sekali lagi mencetak status (4) .

Di tempat yang kami memaparkan status serta-merta selepas menambah tugasan, kami mula-mula menambah tidur 1 saat untuk output terkini.

ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 3; i++) {
            executorService.submit(new Task(i));
        }

        TimeUnit.SECONDS.sleep(1);
            System.out.println(executorService);	//(1)

        TimeUnit.SECONDS.sleep(30);

        executorService.submit(new Task(3));
        TimeUnit.SECONDS.sleep(1);
            System.out.println(executorService);	//(2)

        TimeUnit.SECONDS.sleep(70);

            System.out.println(executorService);	//(3)

        for (int i = 4; i < 7; i++) {
            executorService.submit(new Task(i));
        }

        TimeUnit.SECONDS.sleep(1);
            System.out.println(executorService);	//(4)
        executorService.shutdown();

Dan inilah hasilnya:

Permintaan pengguna diproses #0 pada utas pool-1-thread-1
Permintaan pengguna diproses #1 pada utas pool-1-thread-2
Permintaan pengguna diproses #2 pada utas pool-1-thread-3
(1) java.util.concurrent .ThreadPoolExecutor@f6f4d33[Berjalan, saiz kolam = 3, utas aktif = 0, tugas beratur = 0, tugasan selesai = 3]
Permintaan pengguna diproses #3 pada utas pool-1-thread-2
(2) java.util.concurrent. ThreadPoolExecutor@f6f4d33[Berjalan, saiz kolam = 3, utas aktif = 0, tugas beratur = 0, tugasan selesai = 4] (3) java.util.concurrent.ThreadPoolExecutor@f6f4d33[Berjalan,
saiz kolam = 0, utas aktif = 0 , tugasan beratur = 0, tugas selesai = 4]
Permintaan pengguna diproses #4 pada utas pool-1-thread-4
Permintaan pengguna diproses #5 pada thread pool-1-thread-5
Permintaan pengguna diproses #6 pada utas pool-1-thread-4
(4) java.util.concurrent.ThreadPoolExecutor@f6f4d33[Berjalan, saiz kolam = 2, utas aktif = 0, tugas beratur = 0, tugasan selesai = 7]

Mari kita pergi ke setiap langkah:

Langkah Penjelasan
1 (selepas 3 tugasan selesai) Kami mencipta 3 utas, dan 3 tugasan telah dilaksanakan pada tiga utas ini.
Apabila status dipaparkan, kesemua 3 tugasan selesai, dan benang sedia untuk melaksanakan tugasan lain.
2 (selepas jeda 30 saat dan melaksanakan tugas lain) Selepas 30 saat tidak aktif, benang masih hidup dan menunggu tugasan.
Tugasan lain ditambahkan dan dilaksanakan pada benang yang diambil dari kumpulan benang langsung yang tinggal.
Tiada urutan baharu ditambahkan pada kolam.
3 (selepas jeda 70 saat) Benang telah dikeluarkan dari kolam.
Tiada benang sedia untuk menerima tugasan.
4 (selepas melaksanakan 3 lagi tugasan) Selepas lebih banyak tugasan diterima, urutan baharu telah dicipta. Kali ini hanya dua utas yang berjaya memproses 3 tugasan.

Nah, kini anda sudah mengetahui logik jenis perkhidmatan pelaksana yang lain.

Dengan analogi dengan kaedah lain kelas utiliti Executors , kaedah newCachedThreadPool juga mempunyai versi terlebih muatan yang mengambil objek ThreadFactory sebagai hujah.