包含scheduledthreadpoolexecutor的词条

http://www.itjxue.com  2023-01-26 17:44  来源:未知  点击次数: 

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中的任务后,会反复从阻塞队列中取任务执行。

(责任编辑:IT教学网)

更多

推荐linux文章