<span style="font-size:18px;">比如我有10个任务需要开起线程去服务器上下载东西,你可以写这样的代码:</span><span style="font-size: 18px;"> </span>
class MyTask implements Runnable{ public void run() { } } public static void main(String[] args) { MyTask myTask = new MyTask(); Thread thread = new Thread(myTask); thread.start(); }
时间片
,系统通过一种循环的方式为线程提供时间片,线程在自己的时间内运行,因为时间相当短,多个线程频繁地发生切换,因此给用户的感觉就是好像多个线程同时运行一样,但是如果计算机有多个CPU,线程就能真正意义上的,比如下面的图就很形象的表示了此意思,
既然说到了在开启线程过多的时候,建议使用线程池,那么线程池有什么好处和作用呢?
线程池的作用:
减少频繁的创建和销毁对象。
通常我们创建线程池有二种方式,一种是通过Execuoors帮助类去创建线程池,还有一种就是java给我们提供了一个实现类,就是ThreadPollExector,一般都是用后者,因为更好控制和维护,
那么就讲下这二个类的基本用法:
Executors:jdk1.5之后的一个新类,提供了一些静态工厂,生成一些常用的线程池
,ThreadPoolExecutor是Executors类的底层实现,他常用的方法如下:
1:.newSingleThreadExecutor
创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行>所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池>保证所有任务的执行顺序按照任务的提交顺序执行。
2:newFixedThreadPool
创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
3:newCachedThreadPool
创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程, 那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小
4.newScheduledThreadPool
创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
我们重点是讲ThreadPoolExecutor,它的构造函数比较麻烦,形参比较多,现在就讲下它的形参都是什么意思,因为它会涉及到我们下面讲的一些概念和原理:
//构造方法
public ThreadPoolExecutor(int corePoolSize,//核心池的大小
int maximumPoolSize,//线程池最大线程数
long keepAliveTime,//保持时间
TimeUnit unit,//时间单位
BlockingQueue<Runnable> workQueue,//任务队列
ThreadFactory threadFactory,//线程工厂
RejectedExecutionHandler handler) //异常的捕捉器
现在对几个形参做下解释,如果画图理解,
核心池的大小
,这个参数跟后面讲述的线程池的实现原理有非常大的关系。在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法,从这2个方法的名字就可以看出,是预创建线程的意思,即在没有任务到来之前就创建corePoolSize个线程或者一个线程。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中;线程池最大线程数
,这个参数也是一个非常重要的参数,它表示在线程池中最多能创建多少个线程;表示线程没有任务执行时最多保持多久时间会终止
。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,即当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0; unit:参数keepAliveTime的时间单位
,有7种取值
TimeUnit.DAYS; //天
TimeUnit.HOURS; //小时
TimeUnit.MINUTES; //分钟
TimeUnit.SECONDS; //秒
TimeUnit.MILLISECONDS; //毫秒
TimeUnit.MICROSECONDS; //微妙
TimeUnit.NANOSECONDS; //纳秒
workQueue : 任务队列
,是一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要,会对线程池的运行过程产生重大影响,参考BlockingQueue
ArrayBlockingQueue;
LinkedBlockingQueue;
SynchronousQueue;
threadFactory : 线程工厂
,如何去创建线程的
handler : 任务队列添加异常的捕捉器
,参考 RejectedExecutionHandler
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。 ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程) ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
阻塞队列,如果BlockingQueue是空的,从BlockingQueue取东西的操作将会被阻断进入等待状态,直到BlockingQueue进了东西才会被唤醒,同样,如果BlockingQueue是满的,任何试图往里存东西的操作也会被阻断进入等待状态,直到BlockingQueue里有空间时才会被唤醒继续操作。
基础API介绍
往队列中加元素的方法
从队列中取元素的方法
子类介绍
ArrayBlockingQueue(有界队列)
: FIFO 队列,规定大小的BlockingQueue,其构造函数必须带一个int参数来指明其大小
LinkedBlockingQueue(无界队列)
:FIFO 队列,大小不定的BlockingQueue,若其构造函数带一个规定大小的参数,生成的BlockingQueue有大小限制,若不带大小参数,所生成的BlockingQueue的大小由Integer.MAX_VALUE来决定。
PriorityBlockingQueue
:优先级队列, 类似于LinkedBlockingQueue,但队列中元素非 FIFO, 依据对象的自然排序顺序或者是构造函数所带的Comparator决定的顺序
SynchronousQueue(直接提交策略)
: 交替队列,队列中操作时必须是先放进去,接着取出来,交替着去处理元素的添加和移除
ThreadPoolExecutor.AbortPolicy
当添加任务出错时的策略捕获器,如果出现错误,则直接抛出异常
ThreadPoolExecutor.CallerRunsPolicy
当添加任务出错时的策略捕获器,如果出现错误,直接执行加入的任务
ThreadPoolExecutor.DiscardOldestPolicy
当添加任务出错时的策略捕获器,如果出现错误,移除第一个任务,执行加入的任务
ThreadPoolExecutor.DiscardPolicy
当添加任务出错时的策略捕获器,如果出现错误,不做处理