线程池的细致剖析及简单的面试问题



线程池是啥?面试问题解析

1.首先什么是线程池?

线程池:在我的理解来看,就是一个保存了一定数量线程的空间,系统提前申请好空间和资源,在线程池销毁之前一直等待着被调用。
  在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源。在java的并发开发中,如果说并发的线程数量很多,并且每一个线程都是执行很短的任务就结束的话,这样频繁的创建和销毁线程就会大大的降低系统的效率。
  如此需求,就有了以下的解决办法——线程池:执行完一个任务并不销毁,而是进入休眠状态来等待事件的发生。
  

2.应用场景

  • 需要大量的线程来完成任务,且完成任务的时间较短。例如Web服务器完成网页请求,因为单个任务小且任务量非常的巨大,非常适合线程池技术。可以想一下火爆网站的瞬间访问量。诚然,服务器的处理速度是非常重要的,但是线程池的技术也是重中之重。但是时间较长的任务请求线程池的优点就不明显了。因为这种操作花费的时间和线程的创建销毁时间相比多了很多。

  • 对性能要求苛刻的应用,比如要求服务器迅速响应客户请求。

  • 接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。突发性大量客户请求,在没有线程池情况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限,并出现”OutOfMemory”的错误。

    3.常见的四种线程池和区别

    1)fixThreadPool 正规线程

      这是一个有指定的线程数的线程池,有核心的线程,里面有固定的线程数量,响应的速度快。正规的并发线程,多用于服务器。固定的线程数由系统资源设置。

 public static ExecutorService newFixedThreadPool(int threads)
    {
    return newFixedThreadPool(threads,threads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
    }

  核心线程是没有超时机制的,队列大小没有限制,除非线程池关闭了核心线程才会被回收。

2)caCheThreadPool 缓存线程池

  只有非核心线程,最大线程数很大(Int.Max(values)),它会为每一个任务添加一个新的线程,这边有一个超时机制,当空闲的线程超过60s内没有用到的话,就会被回收。缺点就是没有考虑到系统的实际内存大小。

 public static ExecutorService newCachedThreadPool(int threads)
    {
    return newFixedThreadPool(threads,Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
    }

3)singleThreadPoll 单线程线程池

  只有一个核心线程,将任务通过一个缓存队列持续放到线程中去,完成一个则下一个进入。没有并发性,处理速度慢。会发生阻塞和拥塞。

4)ScheduledThreadPoll 单线程线程池

  这个线程池就厉害了,是唯一一个有延迟执行和周期重复执行的线程池。它的核心线程池固定,非核心线程的数量没有限制,但是闲置时会立即会被回收。


4.问:为什么很多 Java 规范都建议不要显式的创建 Thread,而使用线程池?

答:因为使用线程池的好处是减少在创建和销毁线程上所消耗的时间和系统资源,解决资源不足的问题,如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者过渡切换问题。

5.问:为什么不建议在代码中直接使用Executors创建线程池,而是推荐通过 ThreadPoolExecutor 方式创建?

答:其实不直接使用工具类的目的只有一个,那就是可以明确的让我们知道线程池的运行规则,避免使用工具类的包装而不够直观内部机制而导致潜在的问题。譬如使用 Executors 的 FixedThreadPool 和 SingleThreadPool 创建线程池的原理都允许请求的队列长度为 Integer 的最大值,这样的话可能会堆积大量的请求导致OOM(程序申请内存过大,虚拟机无法满足我们,然后自杀了。);所以推荐直接通过明确的构造参数创建线程池,这样就相当与时刻提醒自己的线程池特性是什么。

猜你喜欢

转载自blog.csdn.net/qunqunstyle99/article/details/81021761