参考:Android开发——Android中常见的4种线程池(保证你能看懂并理解)
参考:干货,谈谈对java线程池的理解(面试必备)
参考:线程池,这一篇或许就够了
使用线程池可以给我们带来很多好处,首先通过线程池中线程的重用,减少创建和销毁线程的性能开销。其次,能控制线程池中的并发数,否则会因为大量的线程争夺CPU资源造成阻塞。最后,线程池能够对线程进行管理,比如使用ScheduledThreadPool来设置延迟N秒后执行任务,并且每隔M秒循环执行一次。
在 Java 中,新建一个线程池对象非常简单,Java 本身提供了工具类java.util.concurrent.Executors,可以使用如下代码创建一个固定数量线程的线程池:
ExecutorService service = Executors.newFixedThreadPool(10);
这样就简单的创建了保护10个线程的线程池。我们来看下内部源码:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}
可以看到是通过ThreadPoolExecutor类来实现创建的。
我们来看看创建一个ThreadPoolExecutor类需要传入那些参数,各个参数的意义是什么。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
...
}
核心运行线程个数:核心线程会在线程池中一直存在,即使这些线程在空闲状态啥事都不做也不会将它们回收掉,当然前提是你没有设置 allowCoreThreadTimeOut 为 true。
SynchronousQueue:这个队列接收到任务的时候,会直接提交给线程处理,而不保留它,如果所有线程都在工作怎么办?那就新建一个线程来处理这个任务!所以为了保证不出现<线程数达到了maximumPoolSize而不能新建线程>的错误,使用这个类型队列的时候,maximumPoolSize一般指定成Integer.MAX_VALUE,即无限大
LinkedBlockingQueue:这个队列接收到任务的时候,如果当前线程数小于核心线程数,则新建线程(核心线程)处理任务;如果当前线程数等于核心线程数,则进入队列等待。由于这个队列没有最大值限制,即所有超过核心线程数的任务都将被添加到队列中,这也就导致了maximumPoolSize的设定失效,因为总线程数永远不会超过corePoolSize
ArrayBlockingQueue:可以限定队列的长度,接收到任务的时候,如果没有达到corePoolSize的值,则新建线程(核心线程)执行任务,如果达到了,则入队等候,如果队列已满,则新建线程(非核心线程)执行任务,又如果总线程数到了maximumPoolSize,并且队列也满了,则发生错误
DelayQueue:队列内元素必须实现Delayed接口,这就意味着你传进去的任务必须先实现Delayed接口。这个队列接收到任务时,首先先入队,只有达到了指定的延时时间,才会执行任务
threadFactory:线程创建工厂
是构造 Thread 的方法,一个接口类,可以使用默认的 default实现,也可以自己去包装和传递,主要实现 newThread 方法即可;
defaultHandler
当参数 maximumPoolSize 达到后丢弃处理的方法实现,java 提供了 5种丢弃处理的方法,当然也可以自己弄,主要是要实现接口 RejectedExecutionHandler 中rejectedExecution(Runnabler, ThreadPoolExecutor e) 方法,java 默认使用的是AbortPolicy,他的作用是当出现这种情况的时候抛出一个异常;通常得到线程池后会调用其中的 submit 或 execute 方法去提交执行异步任务,其实 submit 方法最终会调用execute 方法来进行操作,只是他提供了一个 Future
来托管返回值的处理而已,当你调用需要有返回值的信息时用它来处理是比较好的,这个 Future 会包装 Callable 信息。
package org.godotengine.interview.java;
import androidx.annotation.NonNull;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* @author bob
* Date 19-9-19
* Description
*/
public class ThreadPoolTest {
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = Math.max(4, Math.min(CPU_COUNT - 1, 5));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 2;
private static final BlockingQueue sPoolWorkQueue = new LinkedBlockingQueue<>(1024);
public static Executor THREAD_POOL_MY;
//一共执行20个任务 ,核心线程数是4,最大核心线程数是10,目前加入的runnable20个(相当于20个任务),
//20个任务需要执行,但是核心线程数只有4个,还有16个任务,由于LinkedBlockingQueue队列是最大存放的任务为10 个,队列满了,则会创建新的线程去执行任务,这个时候最大线程是10, 非核心线LinkedBlockingQueue数还有6个,这时候会开6个线程去执行, 目前达到10个最大线程数,此时队列里面还有10个。正好满足队列的大小
static {
System.out.println("核心线程数=" + CORE_POOL_SIZE);
System.out.println("最大线程数=" + MAXIMUM_POOL_SIZE);
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, //核心线程数
MAXIMUM_POOL_SIZE, //线程池中最大的线程数
60, //线程的存活时间,没事干的时候,空闲的时间
TimeUnit.SECONDS, //线程存活时间的单位
sPoolWorkQueue, //线程缓存队列
new ThreadFactory() { //线程创建工厂,如果线程池需要创建线程会调用newThread来创建
@Override
public Thread newThread(@NonNull Runnable r) {
Thread thread = new Thread(r);
thread.setDaemon(false);
return thread;
}
});
threadPoolExecutor.allowCoreThreadTimeOut(true);
THREAD_POOL_MY = threadPoolExecutor;
}
public static void main(String[] args) {
// 创建线程池
ThreadPoolExecutor service = new ThreadPoolExecutor(5, 200,
0L, TimeUnit.MILLISECONDS,
sPoolWorkQueue);
// 等待执行的runnable
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
// 启动的任务数量
int counts = 1000;
for (int i = 0; i < counts; i++) {
service.execute(runnable);
}
// 监控线程池执行情况的代码
ThreadPoolExecutor tpe = ((ThreadPoolExecutor) service);
while (true) {
System.out.println();
int queueSize = tpe.getQueue().size();
System.out.println("当前排队线程数:" + queueSize);
int activeCount = tpe.getActiveCount();
System.out.println("当前活动线程数:" + activeCount);
long completedTaskCount = tpe.getCompletedTaskCount();
System.out.println("执行完成线程数:" + completedTaskCount);
long taskCount = tpe.getTaskCount();
System.out.println("总线程数:" + taskCount);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
线程池的容量与我们启动的任务数量息息相关。
已知:
corePoolSize = 5
maximumPoolSize = 200
workQueue.size() = 1024
我们修改同时 execute 添加到线程池的 Runnable 数量 counts: