一、初识线程池需要掌握以下几点
-
为什么要有线程池?什么是线程池?使用线程池有什么好处?
在多线程的情况下,创建线程的方式有三种详情,并且在run()方法完成后自动回收该线程,但是在实际的操作系统里,如果需要创建上千甚至上百万个线程,创建线程需要时间,销毁的时候也需要时间,但当创建和销毁线程的时间远远大于线程的执行时间时,这样的情况非常不友好,因为内存资源是有限的,大量的线程创建和销毁上下文切换时会抢占CPU,导致线程无法执行,产生out of memory Exception,并且大量的线程回收也会给垃圾回收器带来巨大的压力。
比如此时我创建10000个线程,运行会报出 java.lang.OutOfMemoryError,这种情况非常常见。
public class Demo {
public static void main(String[] args) {
for (int i = 0; i <10000; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
}
为了避免频繁的创建和销毁线程,让已经创建的线程暂时不被销毁,并且可以重复使用,此时产生了线程池的概念
线程池里会存放一部分常用的线程,如果需要哪个线程执行操作,就去线程池里取出线程进行操作,用完即归还到线程池里,省下了创建和销毁线程需要的内存资源,并且线程池存放线程的数量有一定的限制。
如上例,若使用线程池创建10000个线程的话,则不会抛出异常(线程池的创建)
public class Demo {
public static void main(String[] args) {
ExecutorService executor= Executors.newFixedThreadPool(10); //指定线程池最多存放10个线程
for (int i = 0; i <10000; i++) {
Thread t= new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println("启动一个线程");
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
executor.submit(t);
}
}
}
使用线程池的好处:
1)降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗
2)提高响应速度。当任务到达时,任务可以不需要等到线程创建就可以立即执行。
3)提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不但会消耗系统资源。还 会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。
-
线程池的类的继承关系?存在哪些接口,都提供了哪些方法,有什么作用
在java.util.Concurrent包下:
Executor接口: 包含一个execute(Runnable command)方法,(command-可运行的任务)在未来某个时间执行给定的命令。该命令可能在新的线程、已入池的线程或者正调用的线程中执行,这由 Executor 实现决定。
ExecutorService接口:线程池接口,声明了一些管理线程池的方法,execute()/submit()向线程池提交任务,shutdown()/shutdownNow()关闭线程池
Executors类(执行线程的工具),这个类中提供了许多创建线程池的方法,在下文会做以说明
比较重要的类:
①ExecutorService 真正的线程池接口。继承了Executor接口,包含一些常用的方法:shutdown(),shutdownNow(),isShutdown(),isTerminated(),submit(),invokeAny();
②ScheduledExecutorService 能和Timer/TimerTask类似,解决那些需要任务重复执行的问题。
③ThreadPoolExecutor ExecutorService的默认实现。
④ScheduledThreadPoolExecutor 继承ThreadPoolExecutor的ScheduledExecutorService接口实现,周期性任务调度的类实现 -
ThreadPoolExecutor研究,构造函数有哪些?分别什么意思?创建线程池
- 继承关系
ThreadPoolExecutor在java.util.Concurrent包下,继承了AbstractExecutorService类,此类实现了 ExecutorService接口,包含一些常用的方法
public class ThreadPoolExecutor extends AbstractExecutorService
- 属性
//通过原子操作类型截取了高三位。用来表示线程池的状态
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3; //32-3=29
private static final int CAPACITY = (1 << COUNT_BITS) - 1; //容量
// runState is stored in the high-order bits
//-1,正常的状态,可以接受新的任务,处理等待队列中的任务
private static final int RUNNING = -1 << COUNT_BITS;
//0,不接受新的任务提交,但是会继续处理等待队列中的任务
private static final int SHUTDOWN = 0 << COUNT_BITS;
//1,不接受新的任务提交,不再处理等待队列中的任务,中断正在执行任务的线程
private static final int STOP = 1 << COUNT_BITS;
//2,所有的任务都销毁了,workCount 为 0. 线程池的状态在转换为 TIDYING 状态时会执行terminated()
private static final int TIDYING = 2 << COUNT_BITS;
//3,在terminated() 方法结束后,线程池的状态就会变成这个
private static final int TERMINATED = 3 << COUNT_BITS;
//获取当前线程状态
private static int runStateOf(int c) { return c & ~CAPACITY; }
//返回当前线程池中的线程数
private static int workerCountOf(int c) { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }
- 常用参数:
private volatile int corePoolSize
线程池的基本大小,池中所保存的线程数,包括空闲线程。
private volatile int maximumPoolSize
池中允许创建的最大线程数。如果队列满了,并且已创建的线程数小于最大线程数
,则线程池会再创建新的线程执行任务。
private volatile long keepAliveTime
线程活动保持时间。线程池的工作线程空闲后,保持存活的时间。
如果任务很多,并且每个任务执行的时间比较短的话,
可以调大时间提高线程的利用率
unit
keepAliveTime参数-线程活动保持时间的单位。
包括:天(DAYS)/小时(HOURS)/分钟(MINUTES)/毫秒(MILLISECONDS)/
微秒(MICROSECONDS)/纳秒(NANOSECONDS)
private final BlockingQueue<Runnable> workQueue
执行前用于保持任务的队列(可选:ArrayBlockingQueue、LinkedBlockingQueue、
SynchronousQueue、PriorityBlockingQueue)。
此队列仅保持由 execute 方法提交的 Runnable 任务。
private volatile ThreadFactory threadFactory
执行程序创建新线程时使用的工厂。可以通过线程工厂给每个创建出来的线程设置名字。
private volatile RejectedExecutionHandler handler
由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序,拒绝策略
- 构造函数:**
①ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)
用给定的初始参数和默认的线程工厂及被拒绝的执行处理程序创建新的 ThreadPoolExecutor。
②ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler)
用给定的初始参数和默认的线程工厂创建新的 ThreadPoolExecutor。
③ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory)
用给定的初始参数和默认被拒绝的执行处理程序创建新的 ThreadPoolExecutor。
④ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
用给定的初始参数创建新的 ThreadPoolExecutor。
- 内部类Worker
继承了AbstractQueuedSynchronizer类, 实现了Runnable接口
//三个属性:
final Thread thread; //一个线程实例,
Runnable firstTask; //Runnable类型的线程任务
volatile long completedTasks; //volatile修饰的竞争任务
//构造方法
Worker(Runnable firstTask) {
setState(-1); // 现将当前的任务设为running(运行)状态
this.firstTask = firstTask;
//在创建Worker时会调用threadFactory来创建一个线程。
// 启动一个线程就会触发Worker的run方法被线程调用。
this.thread = getThreadFactory().newThread(this);
}
- 创建线程池,通过ThreadPoolExecutor的构造函数
ArrayBlockingQueue<Runnable> arrayBlockingQueue = new ArrayBlockingQueue(10);
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 5, 2, MINUTES, arrayBlockingQueue);
- Executors静态工厂几种常用线程池 及创建方式
1).newCachedThreadPool——创建一个可缓存线程,当线程池大小超过了处理任务所需的线程,那么就会回收部分空闲(一般是60秒无执行)的线程,当有任务来时,可添加新线程来执行。
public class CachedThreadPoolDemo {
public static void main(String[] args) {
ExecutorService CachedThreadPool = Executors.newCachedThreadPool(); //创建一个可缓存线程
for(int i = 0;i < 10;i++ ) {
final int index = i;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
CachedThreadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println("第" +index +"个线程" +Thread.currentThread().getName()+"启动");
}
});
}
}
}
2).newFixedThreadPool—— 创建一个定长线程池,固定大小的线程池,每次提交一个任务就是一个线程,直到达到线程池的最大容量,后面进入等待队列直到前面的任务完成才继续执行
public class FixedThreadPoolDemo {
public static void main(String[] args) {
ExecutorService FixedThreadPool = Executors.newFixedThreadPool(5); //固定大小为5的线程池
for(int i = 0;i < 10;i++ ) {
final int index = i;
FixedThreadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println("当前时间:"+System.currentTimeMillis()+"——"+"第" +index +"个线程" +Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
}
3).newScheduledThreadPool ——创建一个大小无限制的线程池,支持定时和周期性的执行线程
public class ScheduledThreadPoolDemo {
public static void main(String[] args) {
ScheduledExecutorService mScheduledThreadPool = Executors.newScheduledThreadPool(2);
System.out.println("当前时间:" + System.currentTimeMillis());
mScheduledThreadPool.schedule(new Runnable() {
@Override
public void run() {
System.out.println("当前时间:" + System.currentTimeMillis());
}
}, 4, TimeUnit.SECONDS);//这里设置延迟4秒执行
}
}
可以看到当前线程的时间是延迟了4秒才执行
4).newSingleThreadExecutor ——创建一个单线程化的线程池,即线程池中每次只有一个线程工作,单线程串行执行任务
public class SingleThreadExecutorDemo {
public static void main(String[] args) {
ExecutorService SingleThreadPool = Executors.newSingleThreadExecutor();
for(int i = 0;i < 10;i++) {
final int number = i;
SingleThreadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println("当前时间:"+System.currentTimeMillis()+"第"+number+"个线程");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
}
串行运行线程,即先来的先运行。