java springboot線程池以及多線程


線程和進程

進程是資源分配的最小單位,線程是CPU調度的最小單位。

是不是很抽象,做個簡單比喻,進程=火車,線程=車廂,線程在進程里運行(單個的車廂是無法運行的);不同進程之間數據很難共享,同一進程下的線程數據共享則很容易。

多線程

一個應用程序有多條執行路徑(單線程:一個應用程序只有一條執行路徑)。

應用場景

異步,有些功能無需同步執行,可以使用另外一個線程去執行。

多個線程共同完成一個事情,縮短整體執行時間,同時cpu也得到了充分利用(例如,蓋房子壘牆,一個人需要10天,10個人同時做,1天左右可以完成)。

線程池

什么是線程池

線程池,顧名思義,是存放了一堆線程的池子/容器,並且管理線程的整個生命周期。

 java.util.concurrent.Executors提供了一個 java.util.concurrent.Executor接口的實現用於創建線程池。

為什么要用線程池

  1. 減少了創建和銷毀線程的次數,每個工作線程都可以被重復利用,可執行多個任務。
  2. 可以根據系統的承受能力,調整線程池中工作線線程的數目,防止因為消耗過多的內存,而把服務器累趴下(每個線程需要大約1MB內存,線程開的越多,消耗的內存也就越大,最后死機)。

實現方式

此處以springboot實現方式為例

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;


/**
 * @author lipeiguang
 */
@Configuration
@EnableAsync
public class ThreadPoolConfig {
  @Bean(name = "executor")
  public Executor getAsyncThread() {
    ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
    //線程池維護線程的最少數量
    taskExecutor.setCorePoolSize(5);
    //線程池維護線程的最大數量,只有在緩沖隊列滿了之后才會申請超過核心線程數的線程
    taskExecutor.setMaxPoolSize(10);
    //緩存隊列
    taskExecutor.setQueueCapacity(20);
    //允許的空閑時間,當超過了核心線程出之外的線程在空閑時間到達之后會被銷毀
    taskExecutor.setKeepAliveSeconds(200);
    //線程名稱前綴
    taskExecutor.setThreadNamePrefix("my-thread-");
    // 線程池對拒絕任務(無線程可用)的處理策略,目前只支持AbortPolicy、CallerRunsPolicy;默認為后者
    taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    // 調度器shutdown被調用時等待當前被調度的任務完成
    taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
    // 等待時長
    taskExecutor.setAwaitTerminationSeconds(60);
    taskExecutor.initialize();
    return taskExecutor;
  }
}

其中的corePoolSize設置參考:

N為cpu個數

  • 如果是CPU密集型應用,則線程池大小設置為N+1
  • 如果是IO密集型應用,則線程池大小設置為2N+1

cpu個數查詢方式:

Runtime.getRuntime().availableProcessors();

linux查詢方式:

cat /proc/cpuinfo| grep "processor"| wc -l

 

使用線程池實現異步

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.concurrent.Executor;

/**
 * 使用demo
 * @author lipeiguang
 */
@Slf4j
@Api(tags = {"異步線程測試"})
@RestController
public class ExecutorDemo {
    //使用時通過注解
    /**
     * 如果是多線程,可以直接使用這個executor
     */
    @Resource(name = TaskExecutorConstant.SALARY_TASK_EXECUTOR)
    private Executor executor;

    @Autowired
    private ServiceDemo serviceDemo;

    @ApiOperation("異步測試")
    @GetMapping("/test/async")
    public String testAsync(){
        log.info("主線程開始執行。。。。");
        serviceDemo.async();
        log.info("主線程結束執行。。。。");
        return "success";
    }
}
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

/**
 * @author lipeiguang
 */
@Service
@Slf4j
public class ServiceDemo {
    /**
     * 如果只是想異步執行,也可以通過如下注解
     */
    @Async(TaskExecutorConstant.SALARY_TASK_EXECUTOR)
    public void async() {
        log.info("異步線程開始執行。。。");
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("異步線程執行結束。。。");
    }
}

使用線程池實現多線程操作

實現方式有很多種,比如:CountDownLatch,CyclicBarrier,CompletionService等,此處使用CountDownLatch。

測試類:

@Test
    public void testAsyncThreads(){
        log.info("主線程開始執行----------------");
        long start = System.currentTimeMillis();
        taskService.completeWork();
        log.info("主線程結束執行----------------,總耗時:{}ms",(System.currentTimeMillis()-start));
    }

具體多線程處理方法:

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;

/**
 * @author lipeiguang
 */
@Slf4j
@Service
public class TaskService {
    @Autowired
    private Executor executor;

    public void completeWork(){
        int threads = 10;
        List<Boolean> list = new ArrayList<>(threads);
        CountDownLatch countDownLatch = new CountDownLatch(threads);
        for(int i = 0; i < threads; i++){
            executor.execute(()->{
                boolean b = buildHouse();
                list.add(b);
                countDownLatch.countDown();
            });
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        list.forEach(x->{
            log.info("執行結果:{}",x);
        });
    }

    public boolean buildHouse(){
        log.info("開始蓋房子,當前線程:{}",Thread.currentThread().getName());
        try {
            int i = 2000;
            Thread.sleep(i);
            log.info("結束蓋房子,當前線程:{},耗時:{}ms",Thread.currentThread().getName(),i);
            return true;
        } catch (InterruptedException e) {
            log.info("結束蓋房子,當前線程:{}",Thread.currentThread().getName());
            return false;
        }
    }
}

 執行日志如下:

2020-10-02 11:52:10.244  INFO 39124 --- [           main] com.example.demo.DemoApplicationTests    : 主線程開始執行----------------
2020-10-02 11:52:10.246  INFO 39124 --- [  SalaryAsync-2] com.example.demo.service.TaskService     : 開始蓋房子,當前線程:SalaryAsync-2
2020-10-02 11:52:10.247  INFO 39124 --- [  SalaryAsync-8] com.example.demo.service.TaskService     : 開始蓋房子,當前線程:SalaryAsync-8
2020-10-02 11:52:10.246  INFO 39124 --- [  SalaryAsync-5] com.example.demo.service.TaskService     : 開始蓋房子,當前線程:SalaryAsync-5
2020-10-02 11:52:10.247  INFO 39124 --- [ SalaryAsync-10] com.example.demo.service.TaskService     : 開始蓋房子,當前線程:SalaryAsync-10
2020-10-02 11:52:10.247  INFO 39124 --- [  SalaryAsync-6] com.example.demo.service.TaskService     : 開始蓋房子,當前線程:SalaryAsync-6
2020-10-02 11:52:10.246  INFO 39124 --- [  SalaryAsync-3] com.example.demo.service.TaskService     : 開始蓋房子,當前線程:SalaryAsync-3
2020-10-02 11:52:10.246  INFO 39124 --- [  SalaryAsync-1] com.example.demo.service.TaskService     : 開始蓋房子,當前線程:SalaryAsync-1
2020-10-02 11:52:10.247  INFO 39124 --- [  SalaryAsync-7] com.example.demo.service.TaskService     : 開始蓋房子,當前線程:SalaryAsync-7
2020-10-02 11:52:10.248  INFO 39124 --- [  SalaryAsync-9] com.example.demo.service.TaskService     : 開始蓋房子,當前線程:SalaryAsync-9
2020-10-02 11:52:10.246  INFO 39124 --- [  SalaryAsync-4] com.example.demo.service.TaskService     : 開始蓋房子,當前線程:SalaryAsync-4
2020-10-02 11:52:12.250  INFO 39124 --- [ SalaryAsync-10] com.example.demo.service.TaskService     : 結束蓋房子,當前線程:SalaryAsync-10,耗時:2000ms
2020-10-02 11:52:12.250  INFO 39124 --- [  SalaryAsync-8] com.example.demo.service.TaskService     : 結束蓋房子,當前線程:SalaryAsync-8,耗時:2000ms
2020-10-02 11:52:12.250  INFO 39124 --- [  SalaryAsync-5] com.example.demo.service.TaskService     : 結束蓋房子,當前線程:SalaryAsync-5,耗時:2000ms
2020-10-02 11:52:12.250  INFO 39124 --- [  SalaryAsync-6] com.example.demo.service.TaskService     : 結束蓋房子,當前線程:SalaryAsync-6,耗時:2000ms
2020-10-02 11:52:12.250  INFO 39124 --- [  SalaryAsync-2] com.example.demo.service.TaskService     : 結束蓋房子,當前線程:SalaryAsync-2,耗時:2000ms
2020-10-02 11:52:12.251  INFO 39124 --- [  SalaryAsync-3] com.example.demo.service.TaskService     : 結束蓋房子,當前線程:SalaryAsync-3,耗時:2000ms
2020-10-02 11:52:12.251  INFO 39124 --- [  SalaryAsync-9] com.example.demo.service.TaskService     : 結束蓋房子,當前線程:SalaryAsync-9,耗時:2000ms
2020-10-02 11:52:12.251  INFO 39124 --- [  SalaryAsync-1] com.example.demo.service.TaskService     : 結束蓋房子,當前線程:SalaryAsync-1,耗時:2000ms
2020-10-02 11:52:12.251  INFO 39124 --- [  SalaryAsync-7] com.example.demo.service.TaskService     : 結束蓋房子,當前線程:SalaryAsync-7,耗時:2000ms
2020-10-02 11:52:12.251  INFO 39124 --- [  SalaryAsync-4] com.example.demo.service.TaskService     : 結束蓋房子,當前線程:SalaryAsync-4,耗時:2000ms
2020-10-02 11:52:12.251  INFO 39124 --- [           main] com.example.demo.service.TaskService     : 執行結果:true
2020-10-02 11:52:12.251  INFO 39124 --- [           main] com.example.demo.service.TaskService     : 執行結果:true
2020-10-02 11:52:12.251  INFO 39124 --- [           main] com.example.demo.service.TaskService     : 執行結果:true
2020-10-02 11:52:12.251  INFO 39124 --- [           main] com.example.demo.service.TaskService     : 執行結果:true
2020-10-02 11:52:12.251  INFO 39124 --- [           main] com.example.demo.service.TaskService     : 執行結果:true
2020-10-02 11:52:12.251  INFO 39124 --- [           main] com.example.demo.service.TaskService     : 執行結果:true
2020-10-02 11:52:12.251  INFO 39124 --- [           main] com.example.demo.service.TaskService     : 執行結果:true
2020-10-02 11:52:12.251  INFO 39124 --- [           main] com.example.demo.service.TaskService     : 執行結果:true
2020-10-02 11:52:12.251  INFO 39124 --- [           main] com.example.demo.service.TaskService     : 執行結果:true
2020-10-02 11:52:12.251  INFO 39124 --- [           main] com.example.demo.service.TaskService     : 執行結果:true
2020-10-02 11:52:12.251  INFO 39124 --- [           main] com.example.demo.DemoApplicationTests    : 主線程結束執行----------------,總耗時:2007ms

 


免責聲明!

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



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