一 线程
1.创建线程的四种方式:
package com;
import java.util.concurrent.Callable;
/**
* @author syh
* @date 2024/1/2 21:24
* 描述 创建线程的四种方式
*/
public class SyhThread {
/**
* 创建线程的四种方式
*/
public void createThread() {
// 1 new Thread
Thread thread = new Thread(() -> System.out.println("继承Thread类"));
// 2 实现runAble
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("实现runnable接口");
}
};
//3 实现callable接口 --> 有返回值!!主线程使用get方法会阻塞等待子线程完成
Callable callable = new Callable<String>() {
@Override
public String call() throws Exception {
return "实现Callable接口";
}
};
//4 线程池创建
}
}
2.线程的五种基本形态:
1)新建状态 new Thread()
2)就绪状态 调用start方法
3)运行状态 cpu调度
4)阻塞状态 1等待阻塞 (资源抢占,wait等待) 2 同步阻塞 (获取锁阻塞) 3 其他阻塞(比如sleep,IO请求等等)
- 死亡状态 执行结束,或异常退出,结束生命周期
二 线程池
1 线程池有哪几种创建方式?
1>通过java.util.concurrent.Executors工厂类创建:
Executors.newCachedThreadPool():创建一个可缓存线程池,该线程池会根据需要创建新线程,空闲线程会被回收。
Executors.newFixedThreadPool(int nThreads):创建一个定长线程池,包含固定数量的核心线程,并且超出核心线程数的任务将被放入无界队列等待执行。
Executors.newScheduledThreadPool(int corePoolSize):创建一个定长线程池,支持定时及周期性任务执行。
Executors.newSingleThreadExecutor():创建一个单线程化的线程池,确保所有的任务在一个线程上顺序执行。
2>直接实例化java.util.concurrent.ThreadPoolExecutor:
可以手动指定核心线程数、最大线程数、工作队列、线程存活时间、拒绝策略等参数来创建更灵活和定制化的线程池。这种方式提供了对线程池行为的最大控制权。
3>使用Fork/Join框架java.util.concurrent.ForkJoinPool:
适用于处理能够递归分解为子任务的并行计算场景,特别适合于分治算法的实现。
4>Spring框架中的创建方式:
Spring框架提供了一些便捷的方式来配置和管理线程池,例如通过ThreadPoolTaskExecutor
或SimpleAsyncTaskExecutor
等类来配置和创建线程池。
5>自定义线程池:
在某些特殊情况下,开发者可能需要基于上述组件进行扩展或者完全自定义线程池的实现,比如定制特定的阻塞队列、线程工厂、拒绝策略等。
2 线程池的核心参数有哪些?
public class SyhThreadPool {
public ThreadPoolExecutor getThreadPoolExecutor() {
return new ThreadPoolExecutor(
5, //核心线程数
20, //最大线程数
1000, //线程空闲时间
TimeUnit.MILLISECONDS, //时间单位
new LinkedBlockingQueue<>(100), //队列
factory, //创建线程的工厂(非必填)
new ThreadPoolExecutor.AbortPolicy() //拒绝策略 (非必填,此处为默认策略)
);
}
}
1>关于线程池队列的选择
- ArrayBlockingQueue:
- 一个由数组结构组成的有界阻塞队列。一旦创建了固定大小的数组,其容量不可改变。
- 当队列满时,尝试将任务放入队列的线程会被阻塞;当队列空时,获取任务的线程也会被阻塞。
- 特点:适用于处理生产者消费者模型,且对队列大小有明确限制的情况。
- LinkedBlockingQueue:
- 一个基于链表结构的阻塞队列,默认情况下是无界的,但也可以通过构造函数指定容量上限。
- 具有更好的可伸缩性,因为链表不需要预先分配存储空间。
- 特点:适用于处理大量任务或者无法预估任务数量的情况。但如果设置为有界,队列满时会阻止新任务提交。
- SynchronousQueue:
- 不存储元素的阻塞队列,每一个put操作必须等待take操作,反之亦然。
- 它不包含任何内部容量,每个插入操作必须等到另一个线程调用移除操作,否则插入操作将一直阻塞。
- 特点:非常适合传递性场景,即每个任务直接交由执行器立即执行,避免了中间队列存储,可以实现最快速度的任务切换。
- PriorityBlockingQueue:
- 优先级队列,它是一个无界的并发队列,元素按照自然排序或自定义比较器进行排序。
- 插入时,队列会自动根据优先级重新排列元素;取出时,总是取出优先级最高的元素。
- 特点:适用于需要根据优先级处理任务的场景。
2 关于线程池拒绝策略的选择
AbortPolicy(默认策略)
- 当任务被拒绝时,直接抛出一个
RejectedExecutionException
异常,阻止程序继续执行。
java
public static class AbortPolicy implements RejectedExecutionHandler {
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
throw new RejectedExecutionException("Task " + r.toString() + " rejected from " +
executor.toString());
}
}
CallerRunsPolicy
- 使提交任务的线程自己去执行这个任务,而不是拒绝任务。这种方式会降低新任务提交的速度,从而控制任务提交速率,防止任务堆积。
java
public static class CallerRunsPolicy implements RejectedExecutionHandler {
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
if (!executor.isShutdown()) {
r.run();
}
}
}
DiscardPolicy
- 直接丢弃掉被拒绝的任务,不进行任何处理也不会抛出异常,可能导致数据丢失或功能不完整。
java
public static class DiscardPolicy implements RejectedExecutionHandler {
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
// Do nothing.
}
}
DiscardOldestPolicy
- 清除工作队列中最旧的任务(通常是第一个进入队列但还未被执行的任务),然后尝试重新提交当前被拒绝的任务。
java
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
if (!executor.isShutdown()) {
executor.getQueue().poll();
executor.execute(r);
}
}
}