使用任何技术不能为了用而用,要知道使用这门技术的目的以及解决了项目中的哪些痛点。今天来分享下多线程的使用及应用场景。
有哪些应用场景呢?比如我想提高一些不共享数据的流转,像定时任务啊,以及管理页面的导入导出啊,这个时候就需要使用多线程提高处理速度。主要是一个线程不够那我就多用几个的初衷。
还有就是主要业务与次要业务进行异步解耦,大家都知道引入多线程也有异步的功能,类似于消息队列吧,也可以用来业务解耦。比如我处理完主要下单业务你会同步发消息提醒用户么?肯定不会啊,我为啥去同步请求呢,影响我接口响应的时间。
有几种创建线程的方式呢?然后怎么去使用。这是我们最关心的问题,其实创建线程的方式有4种
一、继承 Thread 类
public class Main {
public static void main(String[] args) {
//线程的调用入口
new MyThread().start();
}
}
class MyThread extends Thread {
@Override
public void run() {
//实现自己的业务逻辑
System.out.println(Thread.currentThread().getName() + "t" + Thread.currentThread().getId());
}
}
二、实现Runnable 接口
public class Main {
public static void main(String[] args) {
// 将Runnable实现类作为Thread的构造参数传递到Thread类中,然后启动Thread类
MyRunnable runnable = new MyRunnable();
new Thread(runnable).start();
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "t" + Thread.currentThread().getId());
}
}
三 实现Callable接口,重写call()方法。然后包装成java.util.concurrent.FutureTask, 再然后包装成Thread
Callable:有返回值的线程,能取消线程,可以判断线程是否执行完毕
public class Main {
public static void main(String[] args) throws Exception {
// 将Callable包装成FutureTask,FutureTask也是一种Runnable
MyCallable callable = new MyCallable();
FutureTask<Integer> futureTask = new FutureTask<>(callable);
new Thread(futureTask).start();
// get方法会阻塞调用的线程
Integer sum = futureTask.get();
System.out.println(Thread.currentThread().getName() + Thread.currentThread().getId() + "=" + sum);
}
}
class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName() + "t" + Thread.currentThread().getId() + "t" + new Date() + " tstarting...");
int sum = 0;
for (int i = 0; i <= 100000; i++) {
sum += i;
}
Thread.sleep(5000);
System.out.println(Thread.currentThread().getName() + "t" + Thread.currentThread().getId() + "t" + new Date() + " tover...");
return sum;
}
}
以上都不是推崇的创建线程的方式,最好的是自定义线程池。这样线程的配置自己也能把控。
/**
* @author
* 自定义线程池的配置类
*/
@Configuration
@EnableAsync
public class TaskExecutePool{
@Value("${task.executor.core_pool_size}")
private int corePoolSize;
@Value("${task.executor.max_pool_size}")
private int maxPoolSize;
@Value("${task.executor.queue_capacity}")
private int queueCapacity;
@Value("${task.executor.keep_alive_seconds}")
private int keepAliveSeconds;
@Bean(name = "threadPoolTaskExecutor")
public ThreadPoolTaskExecutor SendTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//配置核心线程数
executor.setCorePoolSize(corePoolSize);
///配置最大线程数
executor.setMaxPoolSize(maxPoolSize);
//配置队列大小
executor.setQueueCapacity(queueCapacity);
/**
* 线程池维护线程所允许的空闲时间--单位秒,超过销毁
* 线程池线程数量大于corePoolSize时候,多出来的空闲线程,多长时间会被销毁
*/
executor.setKeepAliveSeconds(keepAliveSeconds);
//配置线程池中的线程的名称前缀
executor.setThreadNamePrefix("pool-send-task-executor");
/**
* 线程池拒绝策略
*/
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
配置文件中调整线程池参数
task.executor.core_pool_size=10
task.executor.max_pool_size=200
task.executor.queue_capacity=1200
task.executor.keep_alive_seconds=60
调用
@Autowired
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
threadPoolTaskExecutor.execute(() ->service.xxx(param));
到这里我们就可以开箱器即用的使用多线程了,但以上都是入门哦。像线程的中的事务,加入其它线程,等待其它线程等都需要深入研究呢。
然后咱们再看下线程池的拒绝策略有哪些。当pool已经达到max size的时候会采用拒绝策略来处理业务
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。(线程池默认)
ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。如果线程队列已满,则后续提交的任务都会被丢弃,且是静默丢弃
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务。
ThreadPoolExecutor.CallerRunsPolicy:由本地线程处理该任务
声明:文中观点不代表本站立场。本文传送门:https://eyangzhen.com/242929.html