Java--线程池
Java中提供了一系列和锁相关的工具:synchronized、ReentrantLock、AtomicXXX类,它们都能帮助解决我们进行多线程操作时并发问题,之前我们也提到了内核线程模型,Hotspot虚拟机线程的创建都是通过内核线程提供的轻量级进程API创建的,反复的创建线程会不断的由OS线程调度,这也会消耗大量CPU性能
争对创建线程,除了使用Thread之外,Java还提供了线程池类,它们最终都实现的Executor接口,JDK1.7中的继承实现关系如下:
一、使用线程池与Thread对比
使用Thread创建10000个线程,并运行,记录时间:
public class ThreadPoolTest {
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
List<Thread> threads = new ArrayList<>(10000);
for (int i = 0; i < 10000; i++) {
Thread thread = new Thread();
threads.add(thread);
thread.start();
}
for(Thread t:threads){
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("ms:"+(System.currentTimeMillis() - startTime));
}
}
结果:
ms:1761
使用线程池:
public class ThreadPoolTest2 {
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
int size = 10000;
ExecutorService executorService = Executors.newSingleThreadExecutor();
final CountDownLatch countDownLatch = new CountDownLatch(size);
for (int i = 0; i < size; i++) {
executorService.execute(new Runnable() {
@Override
public void run() {
countDownLatch.countDown();
}
});
}
try {
countDownLatch.await();//等待10000计数结束
} catch (InterruptedException e) {
e.printStackTrace();
}
executorService.shutdown();
System.out.println("ms:" + (System.currentTimeMillis() - startTime));
}
}
结果:
ms:69
两者相差的时间为100倍以上,这边使用的线程池回头再来看,由这个例子可以看出,线程的创建到运行过程,会花费大量的cpu时间,争对需要大量并发的操作,使用线程池可以大大提高程序性能
二、ThreadPoolExecutor
1.ThreadPoolExecutor执行任务图解
Executors是个帮助类,内部实际使用的是ThreadPoolExecutor,构造函数如下:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
ThreadPoolExecutor大致的执行场景如下:
execute方法执行流程
2.ThreadPoolExecutor参数
再来看构造函数中各个参数的含义:
-
corePoolSize:核心线程最大数,当有新任务时,如果小于该值,那么会直接创建线程或使用空闲的核心线程执行任务
-
maximumPoolSize:最大线程数,需大于核心线程数,非核心线程最大数 = 最大线程数 - 核心线程最大数,当核心线程全部创建并都在执行任务,那么新任务使用非核心线程执行,同样使用核心线程创建机制:创建线程或使用空闲的线程,非核心线程可以指定死亡时间,相当于临时工
-
keepAliveTime:非核心线程的保持存活时间,当一个非核心线程持续keepAliveTime时间都处于空闲状态,那么使它消亡
-
unit:保持存活时间的时间单位
-
workQueue:任务队列,当线程总数到达最大,并且所有线程都有任务在执行,那么新任务可以进入该队列,等待有线程空闲时执行队列中的任务。通过Executors创建的任务队列,都没有最大值,所以当遇到服务器并发量大的情况,容易OOM,安卓开发中不需要考虑这个问题
-
threadFactory:创建线程工厂,一般使用默认的即可
-
handler:拒绝策略,当触发拒绝时,执行该对象策略策略
3.任务队列
任务队列实现BlockingQueue接口,JDK提供了以下4种:
- ArrayBlockingQueue: 是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。
- LinkedBlockingQueue: 一个基于链表结构的阻塞队列,此队列按FIFO(先进先出)排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列。
- SynchronousQueue: 一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。
- PriorityBlockingQueue: 一个具有优先级的无限阻塞队列。
4.拒绝策略
- DiscardPolicy:丢弃当前将要加入队列的任务本身。
- DiscardOldestPolicy:丢弃任务队列中最旧任务。(丢弃最旧任务也不是简单的丢弃最旧的任务,而是有一些额外的处理)
- AbortPolicy:抛出异常的方式。
- CallerRunsPolicy:不进入线程池执行,由调用者线程执行。
三、Executors
后台一般不允许使用Executors,但我们安卓开发是可以使用的,接下来来使用Executors三种线程池,有了上面ThreadPoolExecutor的了解,理解起来很简单
public class ThreadPoolTest3 {
public static void main(String[] args) {
ExecutorService executorService1 = Executors.newCachedThreadPool();
ExecutorService executorService2 = Executors.newFixedThreadPool(10);
ExecutorService executorService3 = Executors.newSingleThreadExecutor();
for (int i = 1; i <= 100; i++) {
executorService1.execute(new TestRunnable(i));
// executorService2.execute(new TestRunnable(i));
// executorService3.execute(new TestRunnable(i));
}
}
static class TestRunnable implements Runnable {
int i = 0;
public TestRunnable(int i) {
this.i = i;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ":" + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
1.newCachedThreadPool
newCachedThreadPool方法如下:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
它和直接创建线程类似,可以创建无限的线程
2.newFixedThreadPool
newFixedThreadPool方法如下:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
它拥有指定大小的核心线程数,并且只有核心线程,以及一个无限大小的任务队列
3.newSingleThreadExecutor
newSingleThreadExecutor方法如下:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
它仅有一个核心线程,以及一个无限大小的任务队列,所以相当于单线程执行任务队列中的任务