一种最简单得线程创建和回收得方式:
new Thread(new Runnable() {
public void run() {
//do sth
}
}).start();
上述方法在run()方法结束后,自动回收该线程。
线程池
为了避免得创建和销毁线程,我们可以对创建得线程进行复用。
线程池中,总有几个活跃得线程,需要使用线程时,从池中拿一个空闲线程,完成工作后,不急着关闭线程,而是将线程退回线程池,供复用。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ytn9Glf3-1571453622882)(C:\Users\Nice\AppData\Roaming\Typora\typora-user-images\1571406651900.png)]
ThreadPoolExecutor表示一个线程池,Executors类扮演线程池工程得角色。
Executor框架提供了各种类型得线程池
//该方法返回一个固定线程数量得线程池。该该线程池中得数量始终不变。
public static ExecutorService newFixedThreadPool(int nThreads);
//该方法返回一个只有一个线程得线程池。若多余一个任务被提交到线程池,则按FIFO顺序执行任务
public static ExecutorService newSingleThreadExecutor();
//该方法返回一个可根据实际情况调整线程数量得线程池,若有空闲线程可以复用,则优先使用可复用线程。
public static ExecutorService newCachedThreadPool();
//线程池大小为1,ScheduledExecutorService接口扩展了一个给定时间执行某任务得功能。
public static ScheduledExecutorService newSingleThreadScheduledExecutor();
//该方法也返回一个ScheduledExecutorService对象。但可以指定线程数量
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
下面得实例创建了一个由5个线程得线程池,总共提交了十次任务
最终我们会发现,线程是5个先执行,隔了十秒钟,另外5个在执行。
public class ThreadPoolDemo {
public static class MyTask implements Runnable{
public void run() {
System.out.println(System.currentTimeMillis()+": Thread ID: "+
Thread.currentThread().getId());
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
MyTask task = new MyTask();
//创建了一个有5个线程得线程池
ExecutorService es = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
es.submit(task);
}
}
}
计划任务
//在给定得时间delay后,会对任务进行一次调度
public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit);
//剩下两个方法会对任务进行周期性得调度
//initDelay是执行第一个任务之前得延时,而period是前一个任务得开始到后一个任务开始得时间
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initDelay,
long period, TimeUnit unit);
//这里得delay是前一个任务得结束时间到后一个任务得开始时间
public ScheduledFuture<?> scheduleWithFixedRate(Runnable command, long initDelay,
long delay, TimeUnit unit);
//对于后两个函数来说:period = delay + 任务执行得时间
核心线程池得内部实现
核心线程池:newFixedThreadPool()、newSingleThreadExecutor()、newCachedThreadPool()方法。
//corePoolSize和maximumPoolSize大小一样,使用LinkedBlockingQueue任务队列存放无法立即执行得任务,入伍提交过于频繁容易导致资源耗尽。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
//是上个方法得退化,只有一个线程
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
//没有任务时候,线程池中没有线程,任务被提交得时候,任务会加入SynchronousQueue队列,迫使线程池增加新的线程执行任务。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
workQueue:是BlockingQueue接口得对象,仅用于存放Runnable对象:
-
直接提交队列:SynchronousQueue对象提供,该对象没有容量。它提交得任务不会被真实保存,总是将新任务交给线程执行。如果没有空闲进程,则创建新的进行。如果线程到最大值,则执行拒绝策略。
-
有界得任务队列:ArrayBlockingQueue实现,该队列有最大容量maximumPoolSize。若大于corePoolSize,则新任务加入等待队列。若大于maximumPoolSize,则执行拒绝策略。
-
无界得任务队列:LinkedBlockingQueue实现,除非资源耗尽,不然不会出现任务入队失败得情况。
-
优先任务队列:PriorityBlockingQueue实现,可以控制任务执行先后顺序。根据任务自身得优先级顺序先后执行
由以上线程池得实现代码可以看到,它们只是ThreadPoolExecutor类得封装。
public ThreadPoolExecutor(
//指定线程池中得线程数量
int corePoolSize,
//指定线程池中最大线程得数量
int maximumPoolSize,
//当前线程数量数量超过corePoolSize时,多余空闲线程存活时间。即多久会被销毁
long keepAliveTime,
//keepAliveTime得单位
TimeUnit unit,
//任务队列,被提交尚未被执行得任务
BlockingQueue<Runnable> workQueue,
//线程工厂,用于创建线程
ThreadFactory threadFactory,
//拒绝策略,任务太多时如何拒绝任务
RejectedExecutionHandler handler)
//提交任务时候,此时如果线程小于corePoolSize则直接创建新线程执行任务
//当corePoolSize<现在正在运行线程数<maximumPoolSize时候,则进入等待队列
//当maximumPoolSize<现在得线程数,则执行拒绝策略。
下面是ThreadPoolExecutor得核心调度代码
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
//workerCountOf()函数取得了当前线程池得线程总数
if (workerCountOf(c) < corePoolSize) {
//通过addWorker直接调度执行任务
if (addWorker(command, true))
return;
c = ctl.get();
}
//进入等待队列
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);
}
//如果进入等待队列失败,则将任务交给线程池
else if (!addWorker(command, false))
//执行拒绝策略
reject(command);
}
拒绝策略
ThreadPoolExecutor中最后一个参数指定了拒绝策略。
当任务数量超过系统实际承载能力时,调用拒绝策略,是系统超负荷运行时得补救措施。
//该策略会直接抛出异常,阻止系统正常运行
public static class AbortPolicy implements RejectedExecutionHandler;
//只要线程池未关闭,该策略就直接在调用者线程中使用,运行被丢弃得任务
public static class CallerRunsPolicy implements RejectedExecutionHandler;
//默默丢弃无法处理得任务,不予任何处理
public static class DiscardPolicy implements RejectedExecutionHandler;
//丢弃最老得一个请求,并尝试再次提交当前任务。
public static class DiscardOldestPolicy implements RejectedExecutionHandler;
如果上述策略无法满足实际应用需要,那么可以自己扩展RejectedExecutionHandler接口
public interface RejectedExecutionHandler {
void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}
public class RejectThreadPoolDemo {
public static class MyTask implements Runnable{
public void run() {
System.out.println(System.currentTimeMillis()+": Thread ID:"+
Thread.currentThread().getId());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
MyTask task = new MyTask();
//自定义线程池
ExecutorService es = new ThreadPoolExecutor(5, 5, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingDeque<Runnable>(10),
Executors.defaultThreadFactory(),
//自定义拒绝策略
new RejectedExecutionHandler() {
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println(r.toString()+"is discard");
}
});
for (int i = 0; i < Integer.MAX_VALUE; i++) {
es.submit(task);
Thread.sleep(10);
}
}
}
自定义线程创建:ThreadFactory
线程池得主要作用是为了复用,避免线程频繁创建。而最开始得线程便是从:ThreadFactory而来。
public interface ThreadFactory { Thread newThread(Runnable r); }
实例:自定义线程
new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
//创建新线程
Thread t = new Thread(r);
//设置为守护进程
t.setDaemon(true);
//添加打印信息
System.out.println("create"+t);
return t;
}
},
扩展线程池
ThreadPoolExecutor是一个可以扩展得线程池,提供了beforeExecutor()、afterExecutor()、terminated()三个接口对线程池进行控制。