首先是创建线程池,使用的是阿里巴巴规范推荐的ThreadPoolExecutor类来创建线程池。
并且其中的工作队列我使用的是常用的LinkedBlockingQueue来创建FixedThreadpoolExecutor(固定数量的线程池),设置其中的核心线程数,最大线程数,并且队列的容量设置大小和核心线程数一样。
private static BaseThreadPoolExecutor EXECUTOR =
new BaseThreadPoolExecutor(MIN_SIZE, MAX_SIZE,
60L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(BLOCK_SIZE),
new CustomizableThreadFactory("xxx.EXE"));
public BaseThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
}
并且自定义一个runnable类,实现了Runnable接口,jdk就知道这个类是一个线程,决定了线程池创建的线程的类型。线程池创建线程是通过线程池的execute方法创建的。execute内部创建线程并且开始线程。
对实现了runnable接口的自定义类,重写它的run方法,要么重新写一个run_new方法,并且在run方法中运行run_new方法,然后每次在调用线程池的execute方法重写它的run_new方法(我的理解是这样虽然每次都要重写,但是可以减少代码复用,可以在这个业务场景下,实现不同的run方法,以防创建多个runnable类)
Runnable接口,实现run方法。用new Thread(Runnable target).start()方法来启动
@Override
public final void run() {
runTest();
}
public abstract void runTest();
题外话:
与继承Thread类相比,MyThread类在结构上没有区别,除了一点,如果继承了Thread类,同时也直接继承了start方法(),但是如果实现的是我们Runnable接口,并没有start方法可以调用。
不管何种情况下,要想启动多线程,一定依靠Thread类完成,因为要通过thread类的start方法开始执行。
Thread类定义有以下构造方法。
MyThread thread1=new MyThread("ThreadA");
MyThread thread2=new MyThread("ThreadB");
MyThread thread3=new MyThread("ThreadC");
new Thread(thread1).start();
new Thread(thread2).start();
new Thread(thread3).start();
言归正传,如果要经常调用你的方法,那么每次创建新线程可能都不值得,我们使用的线程池进行线程的调用,runnable将需要实现的功能写在run方法中,然后通过线程池的execute()方法调用线程。具体源码就是通过线程池的工作过程,(添加工作线程,看数量超过核心线程数没这种逻辑判断巴拉巴拉)
execute
方法的作用是将传递给它的Runnable
任务放入线程池的工作队列中等待执行。线程池内部会根据可用的线程来选择一个线程去执行这个任务,而不是直接调用Thread.start
启动一个新线程。这是线程池的核心优势之一,它可以更好地控制线程的生命周期和复用已有的线程,避免频繁地创建和销毁线程,提高了性能和资源利用率。
因为线程池源码中的execute方法就是传入一个实现runnable的类(可以不实现就是runnable接口),execute的逻辑是这样的
1.如果运行的线程少于corePoolSize,请尝试以给定的命令作为第一个命令启动一个新线程
任务对addWorker的调用原子地检查runState和workerCount,这样可以防止错误警报
线程,而不应该返回false。
2.如果任务可以成功排队,那么我们仍然需要仔细检查我们是否应该添加一个线程
(因为自上次检查以来已有的死亡)或池在进入此方法后关闭。所以我们
重新检查状态,并在必要时回滚排队已停止,或者启动一个新线程(如果没有)。
3.如果我们不能对任务进行排队,那么我们尝试添加一个新的线如果它失败了,我们就知道我们已经关闭或饱和了,因此拒绝该任务。
然后我在项目中使用多线程就是后台异步处理查询到的数据并根据公式计算最后更新到一张表中,因为需要多次查询多个数据,并且查询的过程比较复杂,如果单线程的话耗时比较长所以使用多线程节约时间。
如果(源码中的workeradded = true)我觉得应该就是可以添加任务,则启动这个线程(start)
然后我在项目中使用多线程就是后台异步处理查询到的数据并根据公式计算最后更新到一张表中,因为需要多次查询多个数据,并且查询的过程比较复杂,如果单线程的话耗时比较长所以使用多线程节约时间。
最后通过定时任务调用这个函数:
定时任务(函数(多线程调用))
为什么不使用executors创建线程池?
Executors创建线程池没有传入阻塞队列的长度(
new LinkedBlockingQueue<>()
),阻塞队列就是一个无边界队列,对于一个无边界队列来说是可以向其中无限添加任务的,这种情况下可能由于任务数太多而导致内存溢出(OOM)。
手写线程池创建线程,并且实现多线程,(传入的线程类是实现runnable接口自定义类)
import java.util.concurrent.*;
class CustomRunnable implements Runnable {
private int taskId;
public CustomRunnable(int taskId) {
this.taskId = taskId;
}
@Override
public void run() {
System.out.println("Thread " + taskId + ": i = " + taskId);
}
}
public class CustomThreadPoolExample {
public static void main(String[] args) {
int numThreads = 20;
ExecutorService executor = new ThreadPoolExecutor(
numThreads, numThreads, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
for (int i = 1; i <= numThreads; i++) {
executor.execute(new CustomRunnable(i));
}
executor.shutdown();
}
}