java併發-線程池ThreadPoolTaskExecutor和ThreadPoolExecutor有何區別?

一、ThreadPoolTaskExecutor和ThreadPoolExecutor有何區別

  • ThreadPoolTaskExecutor是spring core包中的,而ThreadPoolExecutor是JDK中的JUC。
  • ThreadPoolTaskExecutor是對ThreadPoolExecutor進行了封裝處理。
  • ThreadPoolTaskExecutor這個類則是spring包下的,是sring爲我們提供的線程池類。
  • ThreadPoolExecutor結構,祖類都是調用Executor接口,ThreadPoolTaskExecutor結構,祖類是調用Executor接口

二、ThreadPoolTaskExecutor 線程池等待所有任務完成的幾種方式

方式1【推薦】: 使用 CountDownLatch

public class TestThreadPool {

    public static void main(String[] args) throws InterruptedException {

        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
        threadPoolTaskExecutor.setThreadNamePrefix("xxxx-");
        threadPoolTaskExecutor.setCorePoolSize(1);
        threadPoolTaskExecutor.setMaxPoolSize(5);
        threadPoolTaskExecutor.setQueueCapacity(5);
        threadPoolTaskExecutor.setKeepAliveSeconds(300);
        threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy() {
            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
                System.out.println("丟棄");
            }
        });
        threadPoolTaskExecutor.initialize();

        int taskNum = 10;
        CountDownLatch latch =  new CountDownLatch(taskNum);

        for (int i=0; i < taskNum; i++) {
            final int taskId = i;
            System.out.println("-- submit task " + taskId);
            threadPoolTaskExecutor.execute(() -> {
                try {
                    System.out.printf("task: %s, thread: %s, start at %d\n", taskId, Thread.currentThread().getName(), System.currentTimeMillis()/1000);
                    Thread.sleep(2_000);
                    System.out.printf("task: %s, thread: %s, end at %d\n", taskId,  Thread.currentThread().getName(), System.currentTimeMillis()/1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    latch.countDown();
                }
            });
        }

        latch.await();
        threadPoolTaskExecutor.shutdown();
    }
}

方式2: 使用 Future

方式3: setWaitForTasksToCompleteOnShutdown(true)

使用方式: 

spring框架中的ThreadPoolTaskExecutor只是對jdk線程池ThreadPoolExecutor的包裝

第一種使用方法:

1、springboot啓動類中添加線程池配置

	/**
	 * 定義線程池
	 * @return
	 */
	@Bean("testThreadPool")
	public Executor asyncServiceExecutor() {
		ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
		executor.setCorePoolSize(10);//配置核心線程數
		executor.setMaxPoolSize(20);//配置最大線程數
		executor.setKeepAliveSeconds(5);
		executor.setQueueCapacity(200);//配置隊列大小
		executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());//拒絕策略
		executor.setWaitForTasksToCompleteOnShutdown(true);
		executor.initialize();//執行初始化
		return executor;
	}

2、注入ThreadPoolTaskExecutor對象

@Autowired
private ThreadPoolTaskExecutor threadPoolTaskExecutor;

3、其他方法和jdk的線程池使用方式相同。

具體參考:https://blog.csdn.net/sumengnan/article/details/105156397中的demo

第二種使用方法:

1、springboot啓動類中添加線程池配置和@EnableAsync註解

	/**
	 * 定義線程池
	 * @return
	 */
	@Bean("testThreadPool")
	public Executor asyncServiceExecutor() {
		ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
		executor.setCorePoolSize(10);//配置核心線程數
		executor.setMaxPoolSize(20);//配置最大線程數
		executor.setKeepAliveSeconds(5);
		executor.setQueueCapacity(200);//配置隊列大小
		executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());//拒絕策略
		executor.setWaitForTasksToCompleteOnShutdown(true);
		executor.initialize();//執行初始化
		return executor;
	}

2、業務方法上使用@Async註解

package com.sumengnan.test.service;
 
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
 
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
@Service
public class TestServiceImpl {
    //volatile保證了靜態變量的可見性(visibility),
    public static volatile AtomicInteger i = new AtomicInteger(0);
 
    /**
     * 無返回結果
     */
    @Async("testThreadPool")
    public void test() {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("當前第"+ i.incrementAndGet() +"次執行");
    }
 
    /**
     * 有返回結果
     * @return
     */
    @Async("testThreadPool")
    public Future<String> test2() {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return new AsyncResult("當前第"+ i.incrementAndGet() +"次執行");
    }
}

 3、直接調用異步方法即可

package com.sumengnan.test.web;
 
import com.sumengnan.test.service.TestServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
 
import javax.servlet.http.HttpServletRequest;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
 
@RestController
public class TestController {
    @Autowired
    private TestServiceImpl testServiceImpl;
    /**
     * 無返回值
     * @param request
     */
    @GetMapping("test")
    public void test(HttpServletRequest request){
        TestServiceImpl.i=new AtomicInteger(0);
        //循環230次
        for (int i = 0; i <230; i++) {
            System.out.println("第"+(i+1)+"個任務加入隊列");
            testServiceImpl.test();
        }
        System.out.println("已全部加入隊列");
    }
    /**
     * 有返回值
     * @param request
     */
    @GetMapping("test2")
    public void test2(HttpServletRequest request) throws ExecutionException, InterruptedException {
        TestServiceImpl.i=new AtomicInteger(0);
        //循環230次
        for (int i = 0; i <230; i++) {
            System.out.println("第"+(i+1)+"個任務加入隊列");
            Future<String> future = testServiceImpl.test2();
            System.out.println(future.get());
        }
        System.out.println("已全部加入隊列");
    }
}

看結果:

注意:

關於@Async註解失效需要注意以下幾點:

  1. 註解的方法必須是public方法。

 2. 方法一定要從另一個類中調用,也就是從類的外部調用,類的內部調用是無效的。因爲@Transactional和@Async註解的實現都是基於Spring的AOP,而AOP的實現是基於動態代理模式實現的。那麼註解失效的原因就很明顯了,有可能因爲調用方法的是對象本身而不是代理對象,因爲沒有經過Spring容器。

 3. 異步方法使用註解@Async的返回值只能爲void或者Future。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章