1、存在风险的线程池创建方式–慎用
创建线程池的方式多种多样,但下面三种不用用在正式环境中,它们分别是:
这三种创建方式都在Executors工具类中
这些创建线程池的方式都是基于原生创建线程池的方式衍生出来的,我们掌握了原生创建方式,这些创建方式自然也就明白了。
2、Executors线程池工具类
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
核心线程数和最大线程数相同,意味着该线程池中的线程都是核心线程,空闲线程就不会被销毁。任务队列为链式阻塞队列,此队列有资源耗尽的风险,因为LinkedBlockingQueue的容量为Integer的最大值231,意味着任务队列中的任务数量可高达21(2147483657)之多,容易内存爆表。
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
核心线程数和最大线程数相同,都为1,意味着等待唯一的单线程来执行任务,并保证所有任务按照指定顺序(FIFO或优先级)执行。和FixedThreadPool是一样的问题,此队列有资源耗尽的风险。创建单个线程的线程池有两个方法,一个不带线程工厂,一个带线程工厂。
输出结果: "C:\Program Files\Java\jdk1.8.0\bin\java.exe" pool-1-thread-1, index=0 pool-1-thread-1, index=1 pool-1-thread-1, index=2
从运行结果可以看出,所有任务都是在单一线程运行的。
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
可缓存的线程池意味着线程池里面全都是非核心线程最大线程数是Integer的最大值,空闲线程60秒内没工作就会被销毁风险不言而喻,规避资源耗尽的风险。创建可缓存线程的线程池也有两个方法,一个不带线程工厂,一个带线程工厂。
3、提交任务的2种方式
该方法位于Executor接口中,作用是向线程池中提交Runnable任务,Runnable的任务是无返回值的任务。因此该方法 只适合提交无返回值的任务,执行完没有结果返回。如果任务是有返回值的,就需要创建Callable任务,它是一个有返回值的任务,Callable任务执行完,会将执行结果封装到Future对象中,然后返回给调用者,调用者再通过Future对象获取结果。
当该方法提交的任务被拒绝,则抛出任务拒绝异常,提交的任务不能为Null,否则会抛出空指针异常。
两种方法区别如下: