Java开发中多线程的使用梳理(二)

多线程(二):线程池的使用

一、为什么要使用线程池

在java的日常开发中,如果频繁的创建线程,对系统的开销是非常大的,可能会使系统由于过度消耗内存或切换过度而导致系统资源不足。而线程池包含池管理器、工作线程、任务列队、任务接口等模块,可以减少线程的创建和销毁次数,解决线程生命周期开销问题和资源不足问题,从而提高服务器的工作效率。

使用对比

1.线程

public void thread01(int taskCount){
    
    
    for (int i = 0; i < taskCount; i++) {
    
    
        int j = i+1;
        new Thread(()->{
    
    
            try {
    
    
                Thread.sleep(1000);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            System.out.println(System.currentTimeMillis()/1000 +"——执行到第:"+ j +"线程");
        }).start();
    }
}

运行结果:
线程
分析结果:
若taskCount=14,一次性分配14个线程执行

2.线程池

public void pool(int taskCount){
    
    
    ThreadPoolExecutor executor = new ThreadPoolExecutor(5,8,5,TimeUnit.SECONDS,new ArrayBlockingQueue<>(50));
    for (int i = 0; i < taskCount; i++) {
    
    
        int j = i+1;
        Thread thread = new Thread(()->{
    
    
            try {
    
    
                Thread.sleep(1000);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            System.out.println(System.currentTimeMillis()/1000 +"——执行到第:"+ j +"线程");
        });
        executor.execute(thread);
    }
}

运行结果:
线程池
分析结果:
若taskCount=14,线程池中每次只提供5个线程执行,多批次执行。"5+5+4"共三个批次执行

二、使用线程池的三种方式

方式一:使用ThreadPoolExecutor

使用方式如上文"2.线程池",关键参数:
在这里插入图片描述
方式二:Springboot中把线程池纳入到Spring容器管理

第一步:增加配置类(把ThreadPoolExecutor放入Spring容器管理)

@Configuration
public class ThreadPoolExecutorConfig {
    
    
    @Bean
    public ThreadPoolExecutor threadPoolExecutor() {
    
    
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 8, 5, TimeUnit.SECONDS, new ArrayBlockingQueue<>(30));
        executor.allowCoreThreadTimeOut(true);
        return executor;
    }
}

第二步:在使用类中注入ThreadPoolExecutor,使用方式如上文"2.线程池"

@RestController
@RequestMapping("/file")
public class FileController {
    
    
    @Autowired
    private ThreadPoolExecutor executor;  
    //TODO 业务逻辑
} 

方式三:Springboot中使用CompletableFuture

第一步:增加异步配置

@Configuration
@EnableAsync  // 启用异步任务
public class AsyncConfiguration {
    
    
    // 声明一个线程池(并指定线程池的名字)
    @Bean("taskExecutor")
    public Executor asyncExecutor() {
    
    
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //核心线程数5:线程池创建时候初始化的线程数
        executor.setCorePoolSize(5);
        //最大线程数5:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程
        executor.setMaxPoolSize(5);
        //缓冲队列500:用来缓冲执行任务的队列
        executor.setQueueCapacity(500);
        //允许线程的空闲时间60秒:当超过了核心线程出之外的线程在空闲时间到达之后会被销毁
        executor.setKeepAliveSeconds(60);
        //线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池
        executor.setThreadNamePrefix("DailyAsync-");
        executor.initialize();
        return executor;
    }
}

第二步:编写线程service方法,并在方法上加@Async注解

IService

public interface PushDetailService extends IService<PushDetail> {
    
    
    CompletableFuture<String> testThread();
}

ServiceImpl

@Service
public class PushDetailServiceImpl extends ServiceImpl<PushDetailMapper, PushDetail> implements PushDetailService {
    
    
    @Async("taskExecutor")
    public CompletableFuture<String> testThread() {
    
    
        try {
    
    
            Thread.sleep(3000);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        return CompletableFuture.completedFuture("当前时间:"+System.currentTimeMillis());
    }
}

第三步:编写调用的controller方法

@GetMapping("/testThread")
public String testThread() throws ExecutionException, InterruptedException {
    
    
    Long begin = System.currentTimeMillis();
    CompletableFuture<String> future1 = pushService.testThread();
    CompletableFuture<String> future2 = pushService.testThread();
    //join让“主线程”等待“子线程”结束之后才能继续运行
    CompletableFuture.allOf(future1,future2).join();
    log.info("time1 ==> "+future1.get());
    log.info("time2 ==> "+future2.get());
    return System.currentTimeMillis()-begin;
}

猜你喜欢

转载自blog.csdn.net/weixin_50989469/article/details/121697005