머리말
스프링 부팅에서 @async를 사용하여 비동기 작업 및 스레드 풀 컨트롤을 구현하는 것에 관한 기사를 공유했습니다. "스프링 부츠는 @async를 사용하여 비동기 통화를 구현합니다 : 사용자 정의 스레드 풀". 최근에 비동기 작업을 올바르게 처리하지 않아 발생하는 많은 문제를 발견 했으므로 주로 ThreadPooltaskScheduler 스레드 풀의 우아한 스레드 풀에 대해 계속 이야기 할 것입니다.
문제 현상
이전 기사의 4-1-3 장에서, 우리는 스레드 풀을 정의한 다음 @async 주석을 사용하여 3 개의 작업을 작성하고 이러한 작업에서 사용하는 스레드 풀을 실행하는 데 지정했습니다. 위의 단위 테스트에서는 종료 관련 문제에 대해 구체적으로 이야기하지 않았습니다. 문제를 시뮬레이션하고 현장에서 문제를 해결해 봅시다.
1 단계 : 이전과 마찬가지로 ThreadPooltaskScheduler 스레드 풀을 정의합니다.
@SpringBootApplicationPublic Class Application {public static void main (String [] args) {springApplication.run (application.class, args); } @enableAsync @configuration class taskpoolconfig {@bean ( "taskexecutor") 공개 집행자 taskexecutor () {ThreadPoolTaskScheduler Executor = new ThreadPoolTaskScheduler (); executor.setpoolsize (20); executor.setThreadnamEprefix ( "taskexecutor-"); 귀환 집행자; }}}2 단계 : 이전 비동기 작업을 개혁하고 Redis와 같은 외부 리소스에 의존하게합니다.
@slf4j @componentpublic class task {@autowired private StringRedistemplate StringRedistemplate; @async ( "taskexecutor") public void dotaskone ()는 예외 {log.info ( "start task one"); Long Start = System.CurrentTimeMillis (); log.info (StringRedistemplate.randomkey ()); Long End = System.CurrentTimeMillis (); log.info ( "완전한 작업 1, 시간 :" + (종료 - 시작) + "milliseconds"); } @async ( "taskexecutor") public void dotasktwo ()는 예외 {log.info ( "start task 2"); Long Start = System.CurrentTimeMillis (); log.info (StringRedistemplate.randomkey ()); Long End = System.CurrentTimeMillis (); log.info ( "완전한 작업 2, 시간을 찍는 시간 :" + (END -Start) + "milliseconds"); } @async ( "taskexecutor") public void dotaskthree ()는 예외 {log.info ( "시작 작업 3"); Long Start = System.CurrentTimeMillis (); log.info (StringRedistemplate.randomkey ()); Long End = System.CurrentTimeMillis (); log.info ( "완전한 작업 3, 시간 소모 :" + (END -Start) + "milliseconds"); }}참고 : pom.xml에서 종속성을 도입하고 Redis를 구성하는 단계는 여기에서 생략됩니다.
3 단계 : 단위 테스트를 수정하고 동시성에서 종료 상황을 시뮬레이션합니다.
@RunWith (SpringJunit4classRunner.class) @SpringBootTestPublic Class ApplicationTests {@autowired 개인 작업 작업; @test @sneakythrows public void test () {for (int i = 0; i <10000; i ++) {task.dotaskone (); task.dotasktwo (); task.dotaskthree (); if (i == 9999) {System.Exit (0); }}}}참고 : For Loop을 통해 루프를 통해 위에 정의 된 스레드 풀에 작업을 제출합니다. 실행 중에 비동기로 실행되므로 System.Exit (0)를 사용하여 프로그램을 닫으십시오. 이 시점에서 실행중인 작업이 실행되기 때문에 이러한 비동기 작업의 파괴와 스프링 컨테이너의 다른 리소스의 순서가 안전한지 알 수 있습니다.
4 단계 : 위의 단위 테스트를 실행하면 다음 예외 컨텐츠가 발생합니다.
org.springframework.data.redis.redisconnectionFailureException : Jedis 연결을 얻을 수 없습니다. 중첩 예외는 redis.clients.jedis.exceptions.jedisconnectionException입니다. org.springframework.data.redis.connection.jedis.jedis.jedisconnectionfactory.fetchjediscOntector (jedisconnectionfactory.java:204)의 수영장에서 자원을 얻을 수 없습니다. org.springframework.data.redis.connection.jedis.jedisconnectionfactory.getConnection (jedisconnectionfactory.java:348) ~ [Spring-Data-Redis-1.8.10.release.jar : na] at org.springframework.data.redis.core.redisconnectionutils.dogetConnection (readisconnectionutils.java:129) ~ [Spring-data-redis-1.8.10.release.jar : na] at org.spramework.data.redis.corenectionutils (readiscontil.9). ~ [Spring-Data-Redis-1.8.10.release.jar : na] at org.springframework.data.redis.core.redisconnectionUtils.getConnection (readisconnectionUtils.java:79) ~ [Spring-data-Redis-1.8.10.jar : na] at org.springframework.data.redis.core.redistemplate.execute (redistemplate.java:194) ~ [spring-data-redis-1.8.10.release.jar : na] at org.springframework.data.redis.core.redistemplate.execute (redistemplate.java:169). ~ [Spring-Data-Redis-1.8.10.release.jar : na] at org.springframework.data.redis.core.redistemplate.randomkey (redistemplate.java:781) ~ [Spring-data-redis-1.8.10.release.jar : na] at com.didispace.async.task.26). ~ [classs/: na] at com.didispace.async.task $$ fassclassbyspringcglib $$ ca3ff9d6.invoke (<generated>) ~ [classes/: na] at org.springframework.cglib.proxy.methodproxy.invoke (methodproxy.java.204). ~ [Spring-Core-4.3.14. Release.jar : 4.3.14. Release] org.springframework.aop.framework.cglibaopproxy $ cglibmethodinvocation.invokejoinpoint (cglibaopproxy.java:738) ~ [Spring-Aop-4.3.14.release.jar : 4.3.14.14.14.14.14.14.14.14.14.14.14.14.14.14.14.14.14.14.14.14.14.14.14.14.14.14.14. org.springframework.aop.framework.reflectivemethodinvocation.proceed (ReflectiveMethodinVocation.java:157) ~ [Spring-Aop-4.3.14.release.jar : 4.3.14. Release] at org.springframework.aop.interceptor.asyncexecutioninterceptor $ 1. call (asyncexecutioninterceptor.java:115) ~ [spring-aop-4.3.14.release.jar : 4.3.14. release] at java.util.concurrent.futuretask.run (nava.0.0). at java.util.concurrent.scheduledthreadpoolexecutor $ scheduledfuturetask.access $ 201 (scheduledthreadpoolexecutor.java:180) [NA : 1.8.0_151] at java.util.concurrent.scheduledthreadpoolexecutor $ scheduledfuturetask.run (scheduledthreadpoolexecutor.java:293) [na : 1.8.0_151] at java.util.concurrent.concurrent. [NA : 1.8.0_151] java.util.concurrent.threadpoolexecutor $ worker.run (ridepoolexecutor.java:624) [na : 1.8.0_151] at java.lang.thread.run (thread.java:748) [NA : 1.8.0_11). redis.clients.jedis.exceptions.jedisconnectionException : redis.clients.util.pool.getResource (pool.java:53)의 수영장에서 자원을 얻을 수 없음 ~ [jedis-2.9.0.jar : na] at redis.clients.jedis.jedispool.getresource (jedispool.getresource). ~ [jedis-2.9.0.jar : na] at redis.clients.jedis.jedispool.getresource (jedispool.java:16) ~ [jedis-2.9.0.jar : na] at org.springframework.data.redis.connection.jedis.jedisconnectionfactory.fetchjedisconnector (jediscontectionfactory.java:194) ~ [Spring-Data-Redis-1.8.10.Release.jar : NA] ... Java.lang.lang.lang. java.util.concurrent.locks.abstractqueuedsynchronizer $ conditionObject.reportInterRuptAfterwait (AbstractQueuedSynchronizer.java:2014) ~ [NA : 1.8.0_151] at java.util.concurrent.locks.abtractqueuedsynchronizer $ conditionObject.awaitnanos (AbstractQueuedSynchronizer.java:2088) ~ [na : 1.8.0_151] at org.apache.commons.pool.impl.linkedblockingdequ.pollfirst (linkedblockingdup.java:6335). ~ [Commons-Pool2-2.4.3.jar : 2.4.3] at org.apache.commons.pool2.impl.genericobjectpool.borrowobject (genericobjectpool.java:442) ~ [Commons-pool2-2.3.jar : 2.4.3] at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:361) ~[commons-pool2-2.4.3.jar:2.4.3] at redis.clients.util.Pool.getResource(Pool.java:49) ~[jedis-2.9.0.jar:na] ... 22 common frames omitted
그것을 해결하는 방법
원인 분석
예외息JedisConnectionException: Could not get a resource from the pool 응용 프로그램이 닫힐 때 비동기 작업이 여전히 실행되고 있다고 생각하기가 쉽습니다. Redis 연결 풀이 먼저 파괴되기 때문에 위의 오류는 비동기 작업에서 Redis에 액세스하기 위해보고됩니다. 그래서 우리는 응용 프로그램이 닫힐 때 위 구현 방법이 우아하지 않다고 결론지었습니다. 어떻게해야합니까?
해결책
위의 문제를 해결하는 것은 매우 간단합니다. Spring 's ThreadPoolTaskScheduler는 관련 구성을 제공합니다. 다음 설정 만 추가하십시오.
@Bean ( "taskexecutor") 공개 집행자 TaskexeCutor () {ThreadPoolTaskScheduler Executor = New ThreadPoolTaskScheduler (); executor.setpoolsize (20); executor.setThreadnamEprefix ( "taskexecutor-"); executor.setwaitfortaskStocompleteOnShutdown (true); Executor.Setawaitterminationseconds (60); 귀환 집행자;} 참고 : setWaitForTasksToCompleteOnShutdown(true) 이 방법은 여기서 핵심입니다. 다른 콩을 계속 파괴하기 전에 모든 작업이 완료 될 때까지 스레드 풀을 설정하는 데 사용됩니다. 이런 식으로, 이러한 비동기적인 작업의 파괴는 Redis 실 풀의 파괴로 인해 앞에 올 것이다. 동시에, setawaitterminationseconds (60)도 여기에 설정됩니다. 이 방법은 스레드 풀에서 작업의 대기 시간을 설정하는 데 사용됩니다. 이 시간을 초과하면 차단되지 않고 결국 닫을 수 있도록 파괴되기 전에 파괴해야합니다.
Complete example:
독자는 다음과 같은 두 개의 리포지토리를 선택하여 선호도에 따라 4-1-4 프로젝트를 볼 수 있습니다.
github : https://github.com/dyc87112/springboot-learning/
Gitee : https://gitee.com/diidispace/springboot-learning/
로컬 다운로드 : http://xiazai.vevb.com/201805/yuanma/springboot-learning(Vevb.com).rar
요약
위는이 기사의 전체 내용입니다. 이 기사의 내용에 모든 사람의 연구 나 작업에 대한 특정 참조 가치가 있기를 바랍니다. 궁금한 점이 있으면 의사 소통을 위해 메시지를 남길 수 있습니다. Wulin.com을 지원 해주셔서 감사합니다.