多线程的使用

多线程的使用

一.多线程配置文件

1.application中设置多线程配置:

# 线程池相关属性(详细属性在下方进行讲解)
asyncThreadPool.corePoolSize = 10
asyncThreadPool.maxPoolSize = 20
asyncThreadPool.queueCapacity = 50
asyncThreadPool.keepAliveSeconds = 60
asyncThreadPool.awaitTerminationSeconds = 600
asyncThreadPool.threadNamePrefix = async-task-thread-pool-%d

2.config多线程创建

package com.ceshi.config;
​
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
​
import java.util.concurrent.ThreadPoolExecutor;
​
@Configuration
public class ThreadPoolConfig {
    private final static Logger logger = LoggerFactory.getLogger(ThreadPoolConfig.class);
​
    @Value("${asyncThreadPool.corePoolSize}")
    private int corePoolSize;
​
    @Value("${asyncThreadPool.maxPoolSize}")
    private int maxPoolSize;
​
    @Value("${asyncThreadPool.queueCapacity}")
    private int queueCapacity;
​
    @Value("${asyncThreadPool.keepAliveSeconds}")
    private int keepAliveSeconds;
​
    @Value("${asyncThreadPool.awaitTerminationSeconds}")
    private int awaitTerminationSeconds;
​
    @Value("${asyncThreadPool.threadNamePrefix}")
    private String threadNamePrefix;
​
    /**
     * 线程池配置
     * @param
     * @return java.util.concurrent.Executor
     */
    @Bean(name = "threadPoolTaskExecutor")
    public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
        logger.info("---------- 线程池开始加载 ----------");
        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
        //线程池创建时候初始化的线程数
        threadPoolTaskExecutor.setCorePoolSize(corePoolSize);
        //线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程
        threadPoolTaskExecutor.setMaxPoolSize(maxPoolSize);
        //用来缓冲执行任务的队列
        threadPoolTaskExecutor.setQueueCapacity(queueCapacity);
        //当超过了核心线程出之外的线程在空闲时间到达之后会被销毁(秒)
        threadPoolTaskExecutor.setKeepAliveSeconds(keepAliveSeconds);
        //线程池关闭前最大等待时间,确保最后一定关闭
        threadPoolTaskExecutor.setAwaitTerminationSeconds(awaitTerminationSeconds);
        ///线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池
        threadPoolTaskExecutor.setThreadNamePrefix(threadNamePrefix);
        /*
         * 线程池对拒绝任务(无线程可用)的处理策略
         * AbortPolicy:丢弃任务,直接抛出java.util.concurrent.RejectedExecutionException异常,默认的策略
         * CallerRunsPolicy:这个策略重试添加当前的任务,他会自动重复调用 execute() 方法,直到成功
         * DiscardOldestPolicy: 丢弃队列最前面的任务,然后重新尝试执行任务,会导致被丢弃的任务无法再次被执行
         * DiscardPolicy:抛弃当前任务;会导致被丢弃的任务无法再次被执行,但是不抛出异常
         */
        threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 初始化
        threadPoolTaskExecutor.initialize();
        logger.info("---------- 线程池加载完成 ----------");
        return threadPoolTaskExecutor;
    }
}

二.多线程的使用

在一个class类中调用已经在Bean容器中的threadPoolTaskExecutor

@Autowired
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
​
@Scheduled(fixedRate = 1200000)
public void syncCeshi() {
    try{
        List list = new ArrayList<>();
        for (int i = 0;i<=29;i++){
            list.add(i+"");
        }
​
        List ceshi = new ArrayList<>();
​
        for (String id : list) {
            threadPoolTaskExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        //需要执行的业务逻辑
                        long a = System.currentTimeMillis();
                        System.out.println(a+"开始执行--------------"+id);
                        ceshi.add(id+"");
                        long b = System.currentTimeMillis();
                        System.out.println(b-a+"执行结束--------------"+id);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
        }
        //会发现这里输出的是0
        System.out.println(ceshi.size());
    }catch (Exception e){
        e.printStackTrace();
    }
}

三.等待多线程全部执行完毕后执行其他任务

1.CountDownLatch

在多线程环境下,需要保证线程安全,避免出现线程竞争和线程之间的互相干扰。针对这种需要保证多线程运行情况下执行 1000 条 SQL 语句的需求,可以使用 CountDownLatch 来实现线程同步和确保所有的 SQL 查询任务都已经执行完成后再执行后面的代码逻辑。

当前这里对主线程中创建CountDownLatch时,需要设置计算器的大小,这里需要设置为和循环进行多线程执行的大小相同

@Autowired
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
​
@Scheduled(fixedRate = 1200000)
public void syncCeshi() {
    int length = 30;
    try{
        List list = new ArrayList<>();
        for (int i = 0;i ceshi = new ArrayList<>();
​
        CountDownLatch latch = new CountDownLatch(length);
        for (String id : list) {
            threadPoolTaskExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        //需要执行的业务逻辑
                        long a = System.currentTimeMillis();
                        System.out.println(a+"开始执行--------------"+id);
                        ceshi.add(id+"");
                        long b = System.currentTimeMillis();
                        System.out.println(b-a+"执行结束--------------"+id);
                        
                        // CountDownLatch 计数器减 1
                        latch.countDown();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
        }
        
        latch.await();
        //现在这里输出的就是30了
        System.out.println(ceshi.size());
    }catch (Exception e){
        e.printStackTrace();
    }
}

2.awaitTermination

还有一种方法是直接使用executorService自带的方法,用来确保所有的线程任务都执行完毕。

你可能感兴趣的:(java)