springboot多線程


多線程實現

1、要寫一個配置類開啟多線程

配置類實現AsyncConfigurer接口,並重寫getAsyncExecutor()方法返回一個Executor,並用@EnableAsync注解標注。

@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
        // 核心線程大小
        threadPoolTaskExecutor.setCorePoolSize(8);
        // 最大線程大小
        threadPoolTaskExecutor.setMaxPoolSize(16);
        // 隊列大小
        threadPoolTaskExecutor.setQueueCapacity(50);
        // 初始化
        threadPoolTaskExecutor.initialize();
        return threadPoolTaskExecutor;
    }
}

2、在需要多線程執行的方法上標注@Async注解

如果@Async標注在類上,則該類的所有方法都是異步方法。

@Service
@Async
public class AsyncService {

    public void fun1(int i) {
        System.out.println("fun1----" + i);
    }

    public void fun2(int i) {
        System.out.println("fun2====" + i);
    }
}

3、測試效果

@Controller
@RequestMapping("/hello")
public class HelloController {

    @Autowired
    private AsyncService asyncService;

    @GetMapping("/testAsync")
    public String testAsync() {
        for (int i = 0; i < 100; i++) {
            asyncService.fun1(i);
            asyncService.fun2(i);
        }
        return "success";
    }
}

訪問該接口,輸出結果就會看到多線程效果

// 部分輸出結果
fun1----0
fun1----4
fun2====4
fun1----5
fun2====5
fun1----6
fun2====6
fun1----7
fun2====7
fun1----8
fun2====8
fun1----9
fun2====9
fun1----10

實際輸出中的報錯

拋出RejectedExecutionException拒絕執行異常。

java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@24a8a5ec rejected from java.util.concurrent.ThreadPoolExecutor@6719f3a5[Running, pool size = 16, active threads = 16, queued tasks = 50, completed tasks = 0]
	at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063) ~[na:1.8.0_161]
	at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830) [na:1.8.0_161]
	at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379) [na:1.8.0_161]
	at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:134) ~[na:1.8.0_161]

要分析報錯的原因首先要指導Executor的執行原理是什么。

ThreadPoolTaskExecutor的一些參數

在配置類中配置了一些ThreadPoolTaskExecutor的corePoolSize核心線程池、maxPoolSize最大線程池、queneCapacity隊列大小。ThreadPoolTaskExecutor會根據corePoolSize和maxPoolSize來調整線程池大小。

  • 當新任務執行時,如果運行的線程小於corePoolSize就會創建新的線程,即使其他線程是空閑狀態。
  • 如果創建的線程數量達到了corePoolSize,就會把新任務放到隊列中等待執行。
  • 如果隊列滿了,並且運行的線程小於maxPoolSize,就會創建新的線程。
  • 如果線程達到了maxPoolSize,且隊列也滿了,再開啟新的任務就會報錯RejectedExecutionException
  • 如果corePoolSize等於maxPoolSize,表示創建了固定大小的線程池。
  • 如果設置maxPoolSize的值為Integer.MAX_VALUE表示創建了無界的線程池

隊列的三種策略

直接提交

工作隊列的默認選項是synchronousQueue,它將任務直接提交給線程而不保持它們。在此,如果不存在可用於立即運行任務的線程,則試圖把任務加入隊列將失敗,因此會構造一個新的線程。此策略可以避免在處理可能具有內部依賴性的請求集時出現鎖。直接提交通常要求無界maximumPoolSizes以避免拒絕新提交的任務。當命令以超過隊列所能處理的平均數連續到達時,此策略允許無界線程具有增加的可能性。
無界隊列

使用無界隊列(例如,不具有預定義容量的LinkedBlockingQueue)將導致在所有corePoolSize線程都忙時新任務在隊列中等待。這樣,創建的線程就不會超過corePoolSize(因此,maximumPoolSize的值也就無效了)。
有界隊列

當使用有限的maximumPoolSizes時,有界隊列(如ArrayBlockingQueue)有助於防止資源耗盡,但是可能較難調整和控制。隊列大小和最大池大小可能需要相互折衷:使用大型隊列和小型池可以最大限度的降低CPU使用率、操作系統資源和上下文切換開銷,但是可能導致人工降低吞吐量。如果任務頻繁阻塞,則系統可能為超過您許可的更多線程安排時間,使用小型隊列通常要求較大的池大小,CPU使用率較高,但是可能遇到不可接受的調度開銷,這樣可會降低吞吐量。

此外,引發RejectedExecutionException的另一種原因是,顯式的調用了線程池的shutdown()方法。

參考:

https://juejin.im/post/5c344d55f265da617974ff4a

https://blog.csdn.net/wzy_1988/article/details/38922449


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM