new Thread()
咋一看好像就是new了一个对象。
没错是new了个对象,但是不仅仅是普通对象那样在堆中分配了一块内存,它还需要调用操作系统内核API,然后操作系统再为线程分配一些资源。所以较普通对象,线程就比较“重了”。 所以我们要避免频繁的创建和销毁线程,还得控制一下线程的数量。线程池就是用来完成这一项使命的 。
所以多线程就离不开线程池,所以要掌握多线程编程,线程池的了解必不可少。 线程池的设计就是采用生产者-消费者模式,线程池里面的线程是消费者,我们塞给线程池的任务是生产者 。可以理解成线程池就是火车站售票厅,线程池里面的线程就是火车站售票厅窗口员工,我们去买票或者退票改签就是给窗口员工任务也就是生产,然后窗口员工帮我们办理业务,也就是消费。 一般我们是用 ThreadPoolExecutor
来创建线程池,我找了里面参数最多的构造器。
按字面翻译过来就是核心池大小, 其实就是线程池保证有的最小的线程数 ,也就是线程池最少有这么个数量的线程在。也就是火车站售票厅窗口最少的开放数,总得有人在窗口的服务对吧,不能全部都休息。出行平淡期就开几个窗口就够消化这些业务了。 但是java1.6新增了一个allowCoreThreadTimeOut(boolean value)方法,当设为true时候,所有的线程都会超时回收,包括核心线程。
最大线程数,也就是 池里面能有的最大的线程数量 。也就是火车站售票厅窗口所有的窗口都有员工在服务。特别是在节假日的时候,基本上窗口都会开放。
keepAliveTime就是存活时间,TimeUnit是时间单位,来表明keepAliveTime的数字是秒啊还是毫秒啊等等。 这两个参数就是 当我们线程池存在的线程数量超过corePoolSize时,如果有个线程已经空闲了keepAliveTime这么长的时间,那么这个空闲线程就要被回收了 ,就类似于出行高峰期过去了,售票厅窗口可以关闭几个了。总不能都没人了还开这么多窗口把,浪费呀。
工作队列,也就是线程需要执行的Runnable,也就是任务。对应着就是去售票厅排队的我们。
按名字翻译过来就是线程工厂了,也就是我们可以搞个工厂,然后自定义如何创建线程,比如给线程set下名字啊等。然后线程池就会按照工厂定义的方式创建线程。就是如果不设定线程的名字的话,线程名可能就是什么thread-1这样的,对于我们排查问题不太方便,所以 给个名字来标识一下比较好 。
这个是拒绝策略,也就是当线程池中所有的线程都在执行任务,并且工作队列( 是有界队列 )也排满了,那再有任务提交就会执行拒绝策略。ThreadPoolExecutor提供了四种拒绝策略
1、 ThreadPoolExecutor.AbortPolicy()
是默认的拒绝策略,会抛出 RejectedExcecutionException。
2、 ThreadPoolExecutor.CallerRunsPolicy()
让提交任务的线程自己去执行这个任务。。好像这样做挺有道理的..我没空你自己搞去
3、 ThreadPoolExecutor.DiscardOldestPolicy()
丢弃最老的任务,也就是工作队列里最前面的任务,丢弃了之后把新任务加入到工作队列中...真的不公平啊
4、 ThreadPoolExecutor.DiscardPolicy()
直接丢弃任务,并且不抛出任何异常...假装没看到系列
除了这四种还可以自定义拒绝策略, 建议自定义拒绝策略 。因为更加的友好,可以设置成服务降级啊等操作。 ###注意 Java并发包还提供了 Executors
,可以快速创建线程池, 但是不推荐使用 Executors
。因为Executors创建线程池都是默认使用无界队列 LinkedBlockingQueue
,在高负载的情况下容易OOM。所以建议使用有界队列。