@yudesong
2018-02-22T14:04:57.000000Z
字数 4980
阅读 649
线程池
new Thread 的弊端:
线程池的好处:
JDK 为我们内置了4种常见线程池的实现,均可以使用 Executors 工厂类创建。
为了错误避免创建过多线程导致系统奔溃,建议使用有界队列。因为它在无法添加更多任务时会拒绝任务,这样可以提前预警,避免影响整个系统。
执行时间、顺序有要求的话可以选择优先级队列,同时也要保证低优先级的任务有机会被执行。
创建线程池需要使用ThreadPoolExecutor类
public ThreadPoolExecutor(int corePoolSize, //核心线程的数量int maximumPoolSize, //最大线程数量long keepAliveTime, //超出核心线程数量以外的线程空余存活时间TimeUnit unit, //存活时间的单位BlockingQueue<Runnable> workQueue, //保存待执行任务的队列ThreadFactory threadFactory, //创建新线程使用的工厂RejectedExecutionHandler handler // 当任务无法执行时的处理器) {...}
其核心的方法execute
public void execute(Runnable command) {if (command == null)throw new NullPointerException();int c = ctl.get();//1.当前池中线程比核心数少,新建一个线程执行任务if (workerCountOf(c) < corePoolSize) {if (addWorker(command, true))return;c = ctl.get();}//2.核心池已满,但任务队列未满,添加到队列中if (isRunning(c) && workQueue.offer(command)) {int recheck = ctl.get();if (! isRunning(recheck) && remove(command))//如果这时被关闭了,拒绝任务reject(command);else if (workerCountOf(recheck) == 0)//如果之前的线程已被销毁完,新建一个线程addWorker(null, false);}//3.核心池已满,队列已满,试着创建一个新线程else if (!addWorker(command, false))reject(command);//如果创建新线程失败了,说明线程池被关闭或者线程池完全满了,拒绝任务}
以下线程池的主要工作流程:
由于 1 和 3 新建线程时需要获取全局锁,这将严重影响性能。因此 ThreadPoolExecutor 这样的处理流程是为了在执行 execute() 方法时尽量少地执行 1 和 3,多执行 2。
在 ThreadPoolExecutor 完成预热后(当前线程数不少于核心线程数),几乎所有的 execute() 都是在执行步骤 2。
前面提到的 ThreadPoolExecutor 构造函数的参数,分别影响以下内容:
当线程池中的核心线程数已满时,任务就要保存到队列中了。
线程池中使用的队列是 BlockingQueue 接口,常用的实现有如下几种:
public class ThreadPoolManager {private final String TAG = this.getClass().getSimpleName();private static final int CORE_POOL_SIZE = Runtime.getRuntime().availableProcessors() * 2;// 核心线程数为 CPU数*2private static final int MAXIMUM_POOL_SIZE = 64;// 线程队列最大线程数private static final int KEEP_ALIVE_TIME = 1;// 保持存活时间 1秒/*** 如果是要求高吞吐量的,可以使用 SynchronousQueue 队列;如果对执行顺序有要求,可以使用 PriorityBlockingQueue;如果最大积攒的待做任务有上限,可以使用 LinkedBlockingQueue*/private final BlockingQueue<Runnable> mWorkQueue = new LinkedBlockingQueue<>(128);private final ThreadFactory DEFAULT_THREAD_FACTORY = new ThreadFactory() {private final AtomicInteger mCount = new AtomicInteger(1);public Thread newThread(Runnable r) {Thread thread = new Thread(r, TAG + " #" + mCount.getAndIncrement());thread.setPriority(Thread.NORM_PRIORITY);return thread;}};private ThreadPoolExecutor mExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_TIME,TimeUnit.SECONDS, mWorkQueue, DEFAULT_THREAD_FACTORY,new ThreadPoolExecutor.DiscardOldestPolicy());private static volatile ThreadPoolManager mInstance = new ThreadPoolManager();public static ThreadPoolManager getInstance() {return mInstance;}public void addTask(Runnable runnable) {mExecutor.execute(runnable);}@Deprecatedpublic void shutdownNow() {mExecutor.shutdownNow();}}
ExecutorService 提供了两种提交任务的方法:
<T> Future<T> submit(Callable<T> task);<T> Future<T> submit(Runnable task, T result);Future<?> submit(Runnable task);
submit() 有三种重载,参数可以是 Callable 也可以是 Runnable。
同时它会返回一个 Funture 对象,通过它我们可以判断任务是否执行成功。
获得执行结果调用 Future.get()方法,这个方法会阻塞当前线程直到任务完成。
提交一个 Callable 任务时,需要使用 FutureTask 包一层
FutureTask futureTask = new FutureTask(new Callable<String>() { //创建 Callable 任务@Overridepublic String call() throws Exception {String result = "";//do somethingreturn result;}});Future<?> submit = executor.submit(futureTask); //提交到线程池try {Object result = submit.get(); //获取结果} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}
线程池即使不执行任务也会占用一些资源,所以在我们要退出任务时最好关闭线程池。
有两个方法关闭线程池:
它们的共同点是:都是通过遍历线程池中的工作线程,逐个调用 Thread.interrup()来中断线程,所以一些无法响应中断的任务可能永远无法停止(比如 Runnable)