包含scheduledthreadpoolexecutor的词条
ThreadPoolExcutor用法详解
java线程池用法举例:
1、ThreadPoolExecutor executor =new ThreadPoolExecutor(2,10,30, TimeUnit.SECONDS,new ArrayBlockingQueue(100));
2、ThreadPoolExecutor executor2 =new ThreadPoolExecutor(2,10,30, TimeUnit.SECONDS,new LinkedBlockingDeque());
知道了各个参数的作用后,我们开始构造符合我们期待的线程池。首先看JDK给我们预定义的几种线程池:
一、预定义线程池
FixedThreadPool
publicstaticExecutorServicenewFixedThreadPool(intnThreads){returnnewThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,newLinkedBlockingQueue());? ? }
corePoolSize与maximumPoolSize相等,即其线程全为核心线程,是一个固定大小的线程池,是其优势;
keepAliveTime = 0 该参数默认对核心线程无效,而FixedThreadPool全部为核心线程;
workQueue 为LinkedBlockingQueue(无界阻塞队列),队列最大值为Integer.MAX_VALUE。如果任务提交速度持续大余任务处理速度,会造成队列大量阻塞。因为队列很大,很有可能在拒绝策略前,内存溢出。是其劣势;
FixedThreadPool的任务执行是无序的;
适用场景:可用于Web服务瞬时削峰,但需注意长时间持续高峰情况造成的队列阻塞。
CachedThreadPool
publicstaticExecutorServicenewCachedThreadPool(){returnnewThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,newSynchronousQueue());? ? }
corePoolSize = 0,maximumPoolSize = Integer.MAX_VALUE,即线程数量几乎无限制;
keepAliveTime = 60s,线程空闲60s后自动结束。
workQueue 为 SynchronousQueue 同步队列,这个队列类似于一个接力棒,入队出队必须同时传递,因为CachedThreadPool线程创建无限制,不会有队列等待,所以使用SynchronousQueue;
适用场景:快速处理大量耗时较短的任务,如Netty的NIO接受请求时,可使用CachedThreadPool。
SingleThreadExecutor
publicstaticExecutorServicenewSingleThreadExecutor(){returnnewFinalizableDelegatedExecutorService? ? ? ? ? ? (newThreadPoolExecutor(1,1,0L, TimeUnit.MILLISECONDS,newLinkedBlockingQueue()));? ? }
咋一瞅,不就是newFixedThreadPool(1)吗?定眼一看,这里多了一层FinalizableDelegatedExecutorService包装,这一层有什么用呢,写个dome来解释一下:
publicstaticvoidmain(String[] args){? ? ? ? ExecutorService fixedExecutorService = Executors.newFixedThreadPool(1);? ? ? ? ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) fixedExecutorService;? ? ? ? System.out.println(threadPoolExecutor.getMaximumPoolSize());? ? ? ? threadPoolExecutor.setCorePoolSize(8);? ? ? ? ? ? ? ? ExecutorService singleExecutorService = Executors.newSingleThreadExecutor();//? ? ? 运行时异常 java.lang.ClassCastException//? ? ? ThreadPoolExecutor threadPoolExecutor2 = (ThreadPoolExecutor) singleExecutorService;}
对比可以看出,FixedThreadPool可以向下转型为ThreadPoolExecutor,并对其线程池进行配置,而SingleThreadExecutor被包装后,无法成功向下转型。 因此,SingleThreadExecutor被定以后,无法修改,做到了真正的Single。
ScheduledThreadPool
publicstaticScheduledExecutorServicenewScheduledThreadPool(intcorePoolSize){returnnewScheduledThreadPoolExecutor(corePoolSize);? ? }
newScheduledThreadPool调用的是ScheduledThreadPoolExecutor的构造方法,而ScheduledThreadPoolExecutor继承了ThreadPoolExecutor,构造是还是调用了其父类的构造方法。
publicScheduledThreadPoolExecutor(intcorePoolSize){super(corePoolSize, Integer.MAX_VALUE,0, NANOSECONDS,newDelayedWorkQueue());? ? }
二、自定义线程池
ScheduledThreadPoolExecutor删除任务,该怎么解决
这时候想删除其中一个任务,
看了下API,ScheduledThreadPoolExecutor继承ThreadPoolExecutor类,
此里面有个:
-------------------------------------
boolean remove(Runnable task)
从执行程序的内部队列中移除此任务(如果存在),从而如果尚未开始,则其不再运行。
-------------------------------------
我利用下面方法返回任务列表,执行其contains方法,都表示任务列表里面不包含我的任务。
-------------------------------------
BlockingQueueRunnable getQueue()
返回此执行程序使用的任务队列。
-------------------------------------
------解决的方法--------------------------------------------------------
任务列队里的任务实际上对原始的任务做了包装,所以你删除任务的参数必须是你调用scheduleAtFixedRate返回的参数。
------解决的方法--------------------------------------------------------
API对此方法有一个说明:
此方法可用作取消方案的一部分。它可能无法移除在放置到内部队列之前已经转换为其他形式的任务。例如,使用 submit 输入的任务可能被转换为维护 Future 状态的形式。
Timer和ScheduledThreadPoolExecutor的区别
在实际应用中,有时候我们需要创建一些个延迟的、并具有周期性的任务,比如,我们希望当我们的程序启动后每隔1小时就去做一次日志记录。在JDK中提供了两种方法去创建延迟周期性任务。
Timer
Timer是java.util包下的一个类,在JDK1.3的时候被引入,Timer只是充当了一个执行者的角色,真正的任务逻辑是通过一个叫做TimerTask的抽象类完成的,TimerTask也是java.util包下面的类,它是一个实现了Runnable接口的抽象类,包含一个抽象方法run( )方法,需要我们自己去提供具体的业务实现。
Timer类对象是通过其schedule方法执行TimerTask对象中定义的业务逻辑,并且schedule方法拥有多个重载方法提供不同的延迟与周期性服务。
下面是利用Timer去创建的一个延时周期性任务
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class TestTimer {
public static void main(String[] args) {
String time = new SimpleDateFormat("HH:mm:ss").format(new Date());
System.out.println("Start time : " + time);
Timer timer = new Timer();
TimerTask task = new TimerTask() {
@Override
public void run() {
// TODO Auto-generated method stub
String time = new SimpleDateFormat("HH:mm:ss").format(new Date());
System.out.println("Now Time : " + time);
}
}; //end task
timer.schedule(task, 2000, 3000);
}
}
程序的输出:
Start time : 21:36:08
Now Time : 21:36:10
Now Time : 21:36:13
Now Time : 21:36:16
Now Time : 21:36:19
ScheduledThreadPoolExecutor
在JDK1.5的时候在java.util.concurrent并发包下引入了ScheduledThreadPoolExecutor类,引入它的原因是因为Timer类创建的延迟周期性任务存在一些缺陷, ScheduledThreadPoolExecutor继承了ThreadPoolExecutor,并且实现了ScheduledExecutorService接口, ScheduledThreadPoolExecutor也是通过schedule方法执行Runnable任务的。
我们用 ScheduledThreadPoolExecutor来实现和上述Timer一样的功能
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class TestScheduledThreadPoolExecutor {
public static void main(String[] args) {
String time = new SimpleDateFormat("HH:mm:ss").format(new Date());
System.out.println("Start time : " + time);
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(5); //创建5个执行线程
Runnable runnable = new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
String time = new SimpleDateFormat("HH:mm:ss").format(new Date());
System.out.println("Now Time : " + time);
}
};
executor.scheduleWithFixedDelay(runnable, 2, 3, TimeUnit.SECONDS);
}
}
程序的输出:
Start time : 22:12:25
Now Time : 22:12:27
Now Time : 22:12:30
Now Time : 22:12:33
Now Time : 22:12:36
这样看来Timer和 ScheduledThreadPoolExecutor好像没有声明差别,但是 ScheduledThreadPoolExecutor的引入正是由于Timer类存在的一些不足,并且在JDK1.5或更高版本中,几乎没有利用继续使用Timer类,下面说明Timer存在的一些缺点。
单线程
Timer类是通过单线程来执行所有的TimerTask任务的,如果一个任务的执行过程非常耗时,将会导致其他任务的时效性出现问题。而 ScheduledThreadPoolExecutor是基于线程池的多线程执行任务,不会存在这样的问题。
这里我们通过让Timer来执行两个TimerTask任务来说明,其中一个TimerTask的执行过程是耗时的,加入需要2秒。
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class SingleThreadTimer {
public static void main(String[] args) {
String time = new SimpleDateFormat("HH:mm:ss").format(new Date());
System.out.println("Start time : " + time);
Timer timer = new Timer();
TimerTask task1 = new TimerTask() {
@Override
public void run() {
// TODO Auto-generated method stub
String time = new SimpleDateFormat("HH:mm:ss").format(new Date());
System.out.println("Task1 time : " + time);
}
};
TimerTask task2 = new TimerTask() {
@Override
public void run() {
// TODO Auto-generated method stub
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String time = new SimpleDateFormat("HH:mm:ss").format(new Date());
System.out.println("task2 time : " + time);
}
};
timer.schedule(task1, 2000, 1000);
timer.schedule(task2, 2000, 3000);
}
}
这里定义了两个任务,任务1,程序启动2秒后每隔1秒运行一次,任务2,程序启动2秒后,每隔3秒运行1次,然后让Timer同时运行这两个任务
程序的输出如下:
Start time : 22:22:37
Task1 time : 22:22:39
task2 time : 22:22:41
Task1 time : 22:22:41
Task1 time : 22:22:42
task2 time : 22:22:44
Task1 time : 22:22:44
Task1 time : 22:22:45
task2 time : 22:22:47
Task1 time : 22:22:47
Task1 time : 22:22:48
可以分析,无论是任务1还是任务2都没有按照我们设定的预期进行运行,造成这个现象的原因就是Timer类是单线程的。
Timer线程不捕获异常
Timer类中是不捕获异常的,假如一个TimerTask中抛出未检查异常(P.S: java中异常分为两类:checked exception(检查异常)和unchecked exception(未检查异常),对于未检查异常也叫RuntimeException(运行时异常). ),Timer类将不会处理这个异常而产生无法预料的错误。这样一个任务抛出异常将会导致整个Timer中的任务都被取消,此时已安排但未执行的TimerTask也永远不会执行了,新的任务也不能被调度(所谓的“线程泄漏”现象)。
下面就已常见的RuntimeException,ArrayIndexOutOfBoundsException数组越界异常,来演示这个缺点:
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class TestTimerTask {
public static void main(String[] args) {
System.out.println(new SimpleDateFormat("HH:mm:ss").format(new Date()));
Timer timer = new Timer();
TimerTask task1 = new TimerTask() {
@Override
public void run() {
System.out.println("1: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
}
};
TimerTask task2 = new TimerTask() {
@Override
public void run() {
int[] arr = {1,2,3,4,5};
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
int index = (int)(Math.random()*100);
System.out.println(arr[index]);
System.out.println("2: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
}
};
timer.schedule(task1, 2000, 3000);
timer.schedule(task2, 2000, 1000);
}
}
Executors之ScheduledThreadExecutor
ScheduledThreadPoolExecutor 继承自ThreadPoolExecutor实现了ScheduledExecutorService接口。主要完成定时或者周期的执行线程任务。
执行过程:
(1) scheduledExecutorService.schedule(myThread, 5, TimeUnit.SECONDS)
结果:
可以看出主线程启动子线程:scheduledExecutorService.schedule(myThread, 5, TimeUnit.SECONDS)后5S开始thread0,1,2开始执行。 scheduledExecutorService.shutdown();//7s 后关闭不再接受执行线程。shutdown()表示scheduledExecutorService关闭不再接受新的线程执行(如果线程已经开始,则等待此线程结束)这里如果没有 Thread.sleep(70000);则所有线程不会执行。while (!scheduledExecutorService.isTerminated()) {//all thread等待结束 这里是主线程等待所有的子线程结束
}
(2)scheduledExecutorService.scheduleWithFixedDelay(myThread, 5, 2, TimeUnit.SECONDS);
结果:
可以看出线程开始延时5后首次执行,以后每隔2S周期执行任务。
ScheduledThreadPoolExecutor任务超时怎么自动关闭
使用ScheduledThreadPoolExecutor,每隔一个小时去连接指纹机下载考勤记录,有时候指纹机无法返回,导致线程一直在等待,下一个线程无法启动。有没有一个办法,我配置一个超时时间,时间到了,本次任务自动关闭,内存和连接都释放,然后等待下一次的定时到期。
scheduledthreadpoolexecutor 初始化多少个
通过Executors,可以创建3种类型的ThreadPoolExecutor。
- FixedThreadPool
- SingleThreadExecutor
- CachedThreadPool
1.FixedThreadPool
FixedThreadPool被称为可重用固定线程数的线程池。下面是FixedThreadPool的源代码实现。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads, 0L,
TimeUnit.MILLISECONDS, new LinkedBlockingQueueRunnable());
FixedThreadPool中多余的空闲线程会被立即终止。
FixedThreadPool的execute()运行示意图如下所示。
如果当前运行的线程数小于corePoolSize,则创建新线程来执行任务。
当前运行的线程数等于corePoolSize,将任务加入LinkedBlockingQueue。
线程执行完1中的任务后,会反复从阻塞队列中取任务执行。