1. 程式人生 > >springBoot執行緒池

springBoot執行緒池

1:定義執行緒池

1

2

3

4

5

6

7

8

9

10

11

12

13

@EnableAsync

@Configuration

class TaskPoolConfig {

@Bean("taskExecutor")

public Executor taskExecutor() {

ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();

executor.setCorePoolSize(

10);

executor.setMaxPoolSize(20);

executor.setQueueCapacity(200);

executor.setKeepAliveSeconds(60);

executor.setThreadNamePrefix("taskExecutor-");

executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

         executor.setWaitForTasksToCompleteOnShutdown(

true);

executor.setAwaitTerminationSeconds(60);

1

2

3

return executor;

}

}

上面我們通過使用ThreadPoolTaskExecutor建立了一個執行緒池,同時設定了以下這些引數:

  • 核心執行緒數10:執行緒池建立時候初始化的執行緒數
  • 最大執行緒數20:執行緒池最大的執行緒數,只有在緩衝佇列滿了之後才會申請超過核心執行緒數的執行緒
  • 緩衝佇列200:用來緩衝執行任務的佇列
  • 允許執行緒的空閒時間60秒:當超過了核心執行緒出之外的執行緒在空閒時間到達之後會被銷燬
  • 執行緒池名的字首:設定好了之後可以方便我們定位處理任務所在的執行緒池
  • 執行緒池對拒絕任務的處理策略:這裡採用了CallerRunsPolicy策略,當執行緒池沒有處理能力的時候,該策略會直接在 execute 方法的呼叫執行緒中執行被拒絕的任務;如果執行程式已關閉,則會丟棄該任務

說明:setWaitForTasksToCompleteOnShutdown(true)該方法就是這裡的關鍵,用來設定執行緒池關閉的時候等待所有任務都完成再繼續銷燬其他的Bean,這樣這些非同步任務的銷燬就會先於Redis執行緒池的銷燬。同時,這裡還設定了setAwaitTerminationSeconds(60),該方法用來設定執行緒池中任務的等待時間,如果超過這個時候還沒有銷燬就強制銷燬,以確保應用最後能夠被關閉,而不是阻塞住。

2:如何使用該執行緒池呢?

1

2

3

4

5

@Slf4j

@Component

public class Task {

public static Random random = new Random();

    @Autowired

private StringRedisTemplate stringRedisTemplate;

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

@Async("taskExecutor")

public void doTaskOne() throws Exception {

log.info("開始做任務一");

long start = System.currentTimeMillis();

Thread.sleep(random.nextInt(10000));

long end = System.currentTimeMillis();        log.info(stringRedisTemplate.randomKey());

log.info("完成任務一,耗時:" + (end - start) + "毫秒");

}

@Async("taskExecutor")

public void doTaskTwo() throws Exception {

log.info("開始做任務二");

long start = System.currentTimeMillis();

Thread.sleep(random.nextInt(10000));

long end = System.currentTimeMillis();

log.info("完成任務二,耗時:" + (end - start) + "毫秒");

}

@Async("taskExecutor")

public void doTaskThree() throws Exception {

log.info("開始做任務三");

long start = System.currentTimeMillis();

Thread.sleep(random.nextInt(10000));

long end = System.currentTimeMillis();

log.info("完成任務三,耗時:" + (end - start) + "毫秒");

}

}

3 執行非同步任務

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

@RunWith(SpringJUnit4ClassRunner.class)

@SpringBootTest

public class ApplicationTests {

@Autowired

private Task task;

@Test

public void test() throws Exception {

task.doTaskOne();

task.doTaskTwo();

task.doTaskThree();

Thread.currentThread().join();

}

}

1

2

3

4

5

6

2018-03-27 22:01:15.620  INFO 73703 --- [ taskExecutor-1] com.didispace.async.Task                 : 開始做任務一

2018-03-27 22:01:15.620  INFO 73703 --- [ taskExecutor-2] com.didispace.async.Task                 : 開始做任務二

2018-03-27 22:01:15.620  INFO 73703 --- [ taskExecutor-3] com.didispace.async.Task                 : 開始做任務三

2018-03-27 22:01:18.165  INFO 73703 --- [ taskExecutor-2] com.didispace.async.Task                 : 完成任務二,耗時:2545毫秒

2018-03-27 22:01:22.149  INFO 73703 --- [ taskExecutor-3] com.didispace.async.Task                 : 完成任務三,耗時:6529毫秒

2018-03-27 22:01:23.912  INFO 73703 --- [ taskExecutor-1] com.didispace.async.Task                 : 完成任務一,耗時:8292毫秒

4 注意事項

注: @Async所修飾的函式不要定義為static型別,這樣非同步呼叫不會生效

從異常信息JedisConnectionException: Could not get a resource from the pool來看,我們很容易的可以想到,在應用關閉的時候非同步任務還在執行,由於Redis連線池先銷燬了,導致非同步任務中要訪問Redis的操作就報了上面的錯。所以,我們得出結論,上面的實現方式在應用關閉的時候是不優雅的,那麼我們要怎麼做呢?如下設定:

executor.setWaitForTasksToCompleteOnShutdown(true);

executor.setAwaitTerminationSeconds(60);