昨天参加阿里第三轮电话面试---失败了。一个线程池的问题都答错了。
题目:
corePoolSize=5
maximumPoolSize=10
BlockingQueue --size=100
分析线程池的调用过程。先上简单的例子吧package cn.alltb.pool; import java.util.concurrent.*; /** * 线程池提交测试 * Created with hpay. * User: xieronghui * Date: 2016/3/23 * Time: 8:51 */ public class ThreadPooExecutorTest { public static void main(String[] args){ /** * ThreadPoolExecutor(int corePoolSize, // 线程池维护线程的最少数量 int maximumPoolSize, // 线程池维护线程的最大数量 long keepAliveTime, // 线程池维护线程所允许的空闲时间 TimeUnit unit, BlockingQueue<Runnable> workQueue, //BlockingQueue 队列 ThreadFactory threadFactory, // 线程创建工厂 RejectedExecutionHandler handler) // 线程池对拒绝任务的处理策略 */ ExecutorService executorService =new ThreadPoolExecutor(1, 5, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); /** * Thread 采用submit方式提交 * Future<?> submit(Runnable task); */ executorService.submit(new Thread(new Runnable() { @Override public void run() { System.out.println("inner thread method call!"); } })); /** * Callable 支持返回结果 * <T> Future<T> submit(Callable<T> task); */ Future future = executorService.submit(new MyCallAble()); try { System.out.println("采用 Callable task 提交的 获取返回结果:"+future.get(500,TimeUnit.MILLISECONDS)); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } catch (TimeoutException e) { e.printStackTrace(); } /** * void execute(Runnable command); */ executorService.execute(new MyThread()); try { //close pool executorService.shutdown(); executorService.awaitTermination(5, TimeUnit.MICROSECONDS); } catch (InterruptedException e) { e.printStackTrace(); } finally { if (!executorService.isTerminated()) { executorService.shutdownNow(); } } } } class MyCallAble implements Callable{ @Override public Object call() throws Exception { System.out.println("MyCallAble method is call"); return "success_call"; } } class MyThread implements Runnable{ @Override public void run() { System.out.println("MyThread method is call"); } }
运行结果:inner thread method call! MyCallAble method is call 采用 Callable task 提交的 获取返回结果:success_call MyThread method is call==========================================================================================这个例子与问题没有半毛钱关系,但是无关紧要啦。其实核心就是ThreadPoolExecutor对应的execute()方法上个对该方法的源码吧,就直接可以告诉我们 线程池执行的原理,我直接加了注释说明。
public void execute(Runnable command) { if (command == null) throw new NullPointerException(); int c = ctl.get(); /** * 正在运行的线程少于corePoolSize,我们就创建一个新的线程给task **/ if (workerCountOf(c) < corePoolSize) { if (addWorker(command, true)) return; c = ctl.get(); } /** *正在运行的线程大于corePoolSize,检查线程池状态是否是运行的isRunning(c) 并且将task 插入队列中workQueue.offer(command) *如果队列满了则开新线程处理任务workerCountOf(recheck),直到maximumPoolSize ( addWorker(null, false)---》该方法里面有体现) */ 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); } /** * 无法将task 插入到 队列中,试着增加一个新的线程,如果失败,就说明线程池饱和或者关闭,拒绝任务 */ else if (!addWorker(command, false)) reject(command); }
面试题目总结:corePoolSize=5
maximumPoolSize=10
BlockingQueue --size=100当线程池实例化时,我们默认创建了5个线程(corePoolSize=5),当并发不大情况下,我们可以对该线程池中5个线程不断重复利用。当并发大,线程已经消耗 corePoolSize=5 时,程序会将提交的task,插入到队列中(固定长度队列BlockingQueue --size=100 )。假如100个长度的队列都塞满了,程序会自动创建新的线程最大不能大于5个(maximumPoolSize -corePoolSize)。这种境况还是不能满足,就拒绝任务。
扩展:
线程池的作用:线程池就是限制系统中使用线程的数量以及更好的使用线程根据系统的运行情况,可以自动或手动设置线程数量,达到运行的最佳效果:配置少了,将影响系统的执行效率,配置多了,又会浪费系统的资源。用线程池配置数量,其他线程排队等候。当一个任务执行完毕后,就从队列中取一个新任务运行,如果没有新任务,那么这个线程将等待。如果来了一个新任务,但是没有空闲线程的话,那么把任务加入到等待队列中。
创建:很多时候我们可以直接通过
java.util.concurrent.Executors类进行创建,它封装了我们常用的线程池(都是静态public- 所以我们直接很方便的调用了)
实例化还是ThreadPoolExecutor,参照源码。
队列说明:ArrayBlockingQueue: ---Array它是一个数组的队列,我们在创建数组的时候,都需要指定它的长度,同样ArrayBlockingQueue 一样,所以采用这种队列可以 “防止资源耗尽”的情况发生。
LinkedBlockingQueue:是一种基于链表的队列方式,先进先出,表头插入,是无界的 默认大小是 Integer.MAX_VALUE (这样子的话maximumPoolSize 的值也就无效了),所以我们使用的时候一般都固定一个长度。synchronousQueue:它不会保存任务到队列,每一个新增任务的线程必须等待另一个线程取出任务。