Kata pengantar
Saya sebelumnya telah membagikan artikel tentang menggunakan @Async untuk mengimplementasikan tugas asinkron dan kontrol kumpulan utas di boot musim semi: "Spring Boot menggunakan @Async untuk mengimplementasikan panggilan asinkron: kumpulan utas kustom". Karena saya baru -baru ini menemukan banyak masalah yang disebabkan oleh tidak menangani tugas -tugas asinkron dengan benar, saya akan terus berbicara tentang penutupan kolam benang yang elegan, terutama untuk kumpulan utas ThreadPoolTaskscheduler.
Fenomena Masalah
Dalam contoh bab 4-1-3 dari artikel sebelumnya, kami mendefinisikan kumpulan utas, dan kemudian menulis 3 tugas menggunakan anotasi @Async, dan menentukan kumpulan utas yang digunakan oleh tugas-tugas ini untuk dieksekusi. Dalam tes unit di atas, kami tidak secara khusus berbicara tentang masalah yang berhubungan dengan shutdown. Mari kita simulasikan masalah dan hasilkan di situs.
Langkah 1: Seperti sebelumnya, kami mendefinisikan kumpulan utas ThreadPoolTaskScheduler:
@SpringbootApplicationPublic Class Application {public static void main (string [] args) {springApplication.run (application.class, args); } @EnableAsync @configuration class TaskPoolConfig {@bean ("Taskexecutor") Eksekutor publik Taskexecutor () {threadPoolTaskScheduler executor = new ThreadPoolTaskScheduler (); Executor.setPoolSize (20); Executor.setThreadnameprefix ("Taskexecutor-"); Return Executor; }}}Langkah 2: Reformasi tugas asinkron sebelumnya dan membuatnya bergantung pada sumber daya eksternal, seperti Redis
@Slf4j @componentpublic class Task {@Autowired private stringredistemplate StrreDistemplate; @Async ("Taskexecutor") public void dotaskone () melempar Exception {log.info ("Mulai Tugas Satu"); Long Start = System.CurrentTimeMillis (); log.info (stringredistemplate.randomkey ()); long end = system.currentTimemillis (); log.info ("tugas lengkap satu, waktu diambil:" + (end - start) + "milidetik"); } @Async ("Taskexecutor") public void dotasktwo () melempar Exception {log.info ("Mulai Tugas 2"); Long Start = System.CurrentTimeMillis (); log.info (stringredistemplate.randomkey ()); long end = system.currentTimemillis (); log.info ("Tugas Lengkap 2, waktu diambil:" + (end - start) + "milidetik"); } @Async ("Taskexecutor") public void dotaskThree () melempar Exception {log.info ("Mulai tugas tiga"); Long Start = System.CurrentTimeMillis (); log.info (stringredistemplate.randomkey ()); long end = system.currentTimemillis (); log.info ("tugas lengkap tiga, memakan waktu:" + (end - start) + "milidetik"); }}Catatan: Langkah -langkah untuk memperkenalkan dependensi dan mengonfigurasi redis di pom.xml dihilangkan di sini
Langkah 3: Ubah uji unit dan simulasikan situasi shutdown dalam konkurensi tinggi:
@Runwith (springjunit4classrunner.class) @springboottestpublic kelas applicationTests {@autowired tugas tugas pribadi; @Test @sneakythrows public void test () {for (int i = 0; i <10000; i ++) {Task.dotaSkone (); task.dotaSktwo (); Task.dotaSaskThree (); if (i == 9999) {System.exit (0); }}}}Catatan: Dengan melingkar melalui loop untuk, Anda mengirimkan tugas ke kumpulan utas yang ditentukan di atas. Karena dieksekusi secara tidak sinkron, selama eksekusi, gunakan System.exit (0) untuk menutup program. Pada saat ini, karena ada tugas yang dieksekusi, Anda dapat mengamati apakah penghancuran tugas -tugas asinkron ini dan urutan sumber daya lainnya dalam wadah musim semi aman.
Langkah 4: Jalankan uji unit di atas dan kami akan menemukan konten pengecualian berikut.
org.springframework.data.redis.redisconnectionfailureException: tidak bisa mendapatkan koneksi jedis; Pengecualian bersarang adalah redis.clients.jedis.exceptions.jedisconnectionException: tidak bisa mendapatkan sumber daya dari kumpulan di org.springframework.data.redis.connection.jedis.jedisconnectionfactory.fetchjedisconnector (jedisconnectionfactory.java:java:java:java:java:da204 ~ [Spring-data-redis-1.8.10.release.jar: na] di org.springframework.data.redis.connection.jedis.jedisconnectionfactory.getConnection (jedisconnectionfactory.java:348) ~ [Spring-Data-Redis-1.8.10.release: NA] ~ [Spring-Data-Redis-1.8.10.10.rava: na-na] ~ [Spring-Data-Redis-1.8.10.10.10. org.springframework.data.redis.core.redisconnectionutils.dogetConnection (recisconnectionutils.java:129) ~ [Spring-data-redis-1.8.10.release.jar: na] di org.springframework.data.redis.core.redisconnectionutils.getConnection (redisconnectionutils.java:92) ~ [Spring-Data-redis-1.8.10.release.jar: na] di org.springframework.data.redis.core.core.core.core.core.core.core.coConnection ~ [Spring-Data-Redis-1.8.10.release.jar: na] di org.springframework.data.redis.core.redistemplate.execute (redistemplate.java:194) ~ [Spring-Data-redis-1.8.10.release.jar: na] di org.springframework.data.redis.core.redistemplate.execute (redistemplate.java:169) ~ [spring-data-redis-1.8.10.release.jar: na] di org.springframework.data.redis.core.core.core.core.randate.randate.spramorkework ~ [Spring-data-redis-1.8.10.release.jar: na] di com.didispace.async.task.dotaSkone (Task.java:26) ~ [kelas/: na] di com.didispace.async.task $$ fassclassbyspringcglaslib $$ ca3ff9d6.: l nsync.: poSclassbyspringcglash $$ ca3ff9d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6.: l nnff9d6. org.springframework.cglib.proxy.methodproxy.invoke (methodproxy.java:204) ~ [spring-core-4.3.14.release.jar: 4.3.14.release] di org.springframework.aop.framework org.springframework.aop.framework org.springframework. di java.util.concurrent.scheduledThreadPoolExecutor $ scheduledfuturetask.access $ 201 (JadwalThreadPoolExecutor.java:180) [NA: 1.8.0_151] di java.util.concurrent.scheduledThreadPoolExecutor $ scheduledfuturetask.run (dijadwalkanThreadpoolexecutor.java:293) [na: 1.8.0_151] di java.util.concurrent.threadpoolexecutor.runworker (ranta. [na:1.8.0_151] at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_151] at java.lang.Thread.run(Thread.java:748) [na:1.8.0_151]Caused by: redis.clients.jedis.exceptions.jedisconnectionException: tidak bisa mendapatkan sumber daya dari kolam di redis.clients.util.pool.getResource (pool.java:53) ~ [jedis-2.9.0.jar: na] di redis.clients.jedis.jedispool.getrece: ~ [jedis-2.9.0.jar: na] di redis.clients.jedis.jedispool.getResource (jedispool.java:16) ~ [jedis-2.9.0.jar: na] di org.springframework.data.redis.connection.jedis.jedisconnectionfactory.fetchjedisconnector (jedisconnectionfactory.java:194) ~ [Spring-Data-Redis-1.8.10.release.jar: na] ... 19 frame umum yang dihilangkan dengan: Javinglease. java.util.concurrent.locks.abstractqueuedsynchronizer $ conditionObject.reportinterruptafterwait (AbstractQueuedsynchronizer.java:2014) ~ [NA: 1.8.0_151] di java.util.concurrent.locks.Abstractqueuedsynchronizer $ conditionObject.Awaitnanos (AbstractQueuedsynchronizer.java:2088) ~ [na: 1.8.0_151] di org.apache.commons.pool2.impl.linkedblockinging.pollfiring.pool.pool2.Impl.linkedblockinging.pollfiring ~ [commons-pool2-2.4.3.jar: 2.4.3] di org.apache.commons.pool2.impl.genericobjectpool.borrowObject (genericObjectpool.java:442) ~ [commons-pool2-2.4.4.jar: 2.4.3] di org.apache.commons.pool2.impl.genericobjectpool.borrowObject (genericObjectpool.java:361) ~ [commons-pool2-2.4.3.jar: 2.4.3] di redis.clients.util.pool.getResource (pool.java:49) ~ [jedis-oM.
Bagaimana menyelesaikannya
Penyebab Analisis
Dari息JedisConnectionException: Could not get a resource from the pool , mudah bagi kita untuk berpikir bahwa tugas asinkron masih dieksekusi ketika aplikasi ditutup. Karena kumpulan koneksi Redis dihancurkan terlebih dahulu, kesalahan di atas dilaporkan dalam tugas asinkron untuk mengakses Redis. Jadi, kami menyimpulkan bahwa metode implementasi di atas tidak elegan ketika aplikasi ditutup, jadi apa yang harus kami lakukan?
Larutan
Sangat mudah untuk menyelesaikan masalah di atas. Thread Spring's ThreadPoolTaskScheduler memberi kami konfigurasi yang relevan, cukup tambahkan pengaturan berikut:
@Bean ("Taskexecutor") Public Executor Taskexecutor () {ThreadPoolTaskScheduler Executor = New ThreadPoolTaskScheduler (); Executor.setPoolSize (20); Executor.setThreadnameprefix ("Taskexecutor-"); executor.setwaitfortAskStocompleteonshutdown (true); Executor.setawaitterminationsConds (60); mengembalikan pelaksana;} CATATAN: setWaitForTasksToCompleteOnShutdown(true) Metode ini adalah kunci di sini. Ini digunakan untuk mengatur kumpulan utas untuk menunggu semua tugas diselesaikan sebelum terus menghancurkan kacang lainnya. Dengan cara ini, penghancuran tugas -tugas asinkron ini akan didahului oleh penghancuran kumpulan benang Redis. Pada saat yang sama, setawaitterminationseconds (60) juga ditetapkan di sini. Metode ini digunakan untuk mengatur waktu tunggu tugas di kumpulan utas. Jika melebihi saat ini, akan dipaksa untuk menghancurkannya sebelum dihancurkan untuk memastikan bahwa aplikasi dapat ditutup pada akhirnya daripada diblokir.
Contoh Lengkap:
Pembaca dapat memilih dua repositori berikut untuk melihat proyek Bab4-1-4 sesuai dengan preferensi mereka:
Github: https://github.com/dyc87112/springboot-learning/
Gitee: https://gitee.com/diidispace/springboot-learning/
Unduh Lokal: http://xiazai.vevb.com/201805/yuanma/springboot-learning(vevb.com).rar
Meringkaskan
Di atas adalah seluruh konten artikel ini. Saya berharap konten artikel ini memiliki nilai referensi tertentu untuk studi atau pekerjaan semua orang. Jika Anda memiliki pertanyaan, Anda dapat meninggalkan pesan untuk berkomunikasi. Terima kasih atas dukungan Anda ke wulin.com.