ScheduleThreadPoolExecutor的工作原理与使用示例

欢迎探讨,如有错误敬请指正

如需转载,请注明出处  http://www.cnblogs.com/nullzx/


 

1. ScheduleExecutorService接口、ScheduledFuture接口

ScheduleExecutorService ScheduledFuture

从图中可以看出ScheduledExecutorService接口继承了ExecutorService接口,同时还添加了有关提交定时任务的四个方法。

public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit)
//向定时任务线程池提交一个延时Runnable任务(仅执行一次)
public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit);
//向定时任务线程池提交一个延时的Callable任务(仅执行一次)
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, 
                                                  long period, TimeUnit unit)
//向定时任务线程池提交一个固定时间间隔执行的任务
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay,
                                                      long delay, TimeUnit unit);
//向定时任务线程池提交一个固定延时间隔执行的任务

固定时间间隔的任务不论每次任务花费多少时间,下次任务开始执行时间是确定的,当然执行任务的时间不能超过执行周期。

固定延时间隔的任务是指每次执行完任务以后都延时一个固定的时间。由于操作系统调度以及每次任务执行的语句可能不同,所以每次任务执行所花费的时间是不确定的,也就导致了每次任务的执行周期存在一定的波动。

注意:定时或延时任务中所涉及到时间、周期不能保证实时性及准确性,实际运行中会有一定的误差。

从上图可以还可以看出ScheduledThreadPoolExecutor还直接继承了ThreadPoolExecutor。这样做是为了利用ThreadPoolExecutor已实现的方法。

可以向定时任务线程池提交普通任务。对于定时任务线程池而言,普通任务只不过是延时执行时间为0,周期为0的任务。

从上述四个方法中的返回值可以看出,当向线程池提交任务时会返回一个ScheduleFuture接口的对象。通过下图可以看出,ScheduledFuture接口继承了Delayed和Future接口。我们可以通过ScheduleFutured对象的cancel方法可以结束一个定时任务。

 

SFT

在ScheduledThreadPoolExecutor中阻塞队列存储的是ScheduledFutureTask对象,它是任务真正的载体。但是ScheduledFutureTask对象同样可以看做Runnable对象,所以同样可以使用ThreadPoolExecutor中的方法来处理它。

2. ScheduledThreadPoolExecutor的运行原理

定时任务线程池工作原理

在图中,我们将ScheduleFutureTask简称为SFT

在定时任务线程池中存储任务的队列是具有优先性质的阻塞队列。在这个队列中离下次执行时间最近的任务位于队首(关于优先队列的原理请参照本博客后续数据结构的相关内容)。线程每次通过优先队列的take方法获取任务。如果队列为空take方法阻塞,否则take方法从队首获取带执行的任务(即ScheduleFutureTask对象),然后从任务的getDelay方法获取应当延时的时间,再通过带参数的await方法进行延时。当延时时间已到,则开始执行任务(在执行之前还要检测这个任务是否已被取消),执行完毕以后继续判断这是否是一个周期任务。如果不是,调用take方法获取新任务,重复上述操作。如果是计算下一次执行的时间,然后调用队列的add方法将这个任务再次入列。入列完成后,继续调用take方法获取新任务,这样重复的执行下去。

以上就是定时任务线程池工作的最基本原理,实际上take、add、任务的取消等过程比较复杂,图中也没有说明线程在执行await方法时队首元素改变时的情况。如果想继续了解,请参阅本博客“ScheduledThreadPoolExecutor源代码分析”的文章。

3. ScheduleThreadPoolExecutor与Timer相比的优势。

(1)Timer是基于绝对时间的延时执行或周期执行,当系统时间改变,则任务的执行会受到的影响。而ScheduleThreadPoolExecutore中,任务时基于相对时间进行周期或延时操作。

(2)Timer也可以提交多个TimeTask任务,但只有一个线程来执行所有的TimeTask,这样并发性受到影响。而ScheduleThreadPoolExecutore可以设定池中线程的数量。

(3)Timer不会捕获TimerTask的异常,只是简单地停止,这样势必会影响其他TimeTask的执行。而ScheduleThreadPoolExecutore中,如果一个线程因某些原因停止,线程池可以自动创建新的线程来维护池中线程的数量。

4. 使用示例

package javaleanning;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

public class ScheduledThreadPoolExecutorDemo {
	static class TimerTask implements Runnable{
		private String id;
		public TimerTask(String id){
			this.id = id;
		}
		@Override
		public void run(){
			System.out.println(id);
		}
	}
	
	public static void main(String[] args) throws InterruptedException{
		ScheduledExecutorService ses = Executors.newScheduledThreadPool(2);
		ScheduledFuture sfa = ses.scheduleAtFixedRate(new TimerTask("a"), 200,
				                                    1000, TimeUnit.MILLISECONDS);
		ScheduledFuture sfb = ses.scheduleAtFixedRate(new TimerTask("b"), 400, 
				                                    1000, TimeUnit.MILLISECONDS);
		ScheduledFuture sfc = ses.scheduleAtFixedRate(new TimerTask("c"), 600,
				                                    1000, TimeUnit.MILLISECONDS);
		ScheduledFuture sfd = ses.scheduleAtFixedRate(new TimerTask("d"), 800, 
				                                    1000, TimeUnit.MILLISECONDS);
		Thread.sleep(5000);
		sfa.cancel(true);
		Thread.sleep(5000);
		ses.shutdown();
	}
}

 在这个示例中,定义了一个内部类TimerTask,它实现了Runnable接口。在main方法中创建了一个定时任务线程池,向它提交了四个任务a,b,c,d。四个任务起始的延时时间分别是200ms、400ms、600ms、800ms,执行周期都为1000ms。5秒以后取消了定时任务a的执行。又过了5秒,关闭了线程池。默认情况下关闭线程池会结束池所有的任务。

以下是运行结果
a
b
c
d
a
b
c
d
a
b
c
d
a
b
c
d
a
b
c
d
b
c
d
b
c
d
b
c
d
b
c
d
b
c
d

5. 参考内容

[1] http://janeky.iteye.com/blog/770441

转载于:https://www.cnblogs.com/nullzx/p/5188890.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
VLAN(Virtual Local Area Network)是一种逻辑上的划分技术,用于将单个物理局域网(LAN)划分为多个虚拟局域网。每个VLAN都是一个独立的广播域,可以在网络中实现隔离和安全性。 VLAN的工作原理如下: 1. 交换机将端口根据所属的VLAN进行划分,每个端口可以属于一个或多个VLAN。 2. 当设备发送数据时,交换机会根据数据包的VLAN标签来决定将数据包转发到哪个VLAN中的端口。 3. 在同一个VLAN中的设备可以直接进行通信,而不需要经过路由器。 4. 如果需要不同VLAN之间的通信,需要通过路由器或三层交换机来实现。 配置VLAN的命令取决于交换机的厂商和型号,下面是一些常用的配置命令示例: 1. 创建VLAN: - Cisco交换机:`vlan <vlan_id>` - H3C交换机:`vlan <vlan_id>` 2. 将端口划分到指定VLAN: - Cisco交换机:`interface <interface_id>`,然后使用`switchport mode access`和`switchport access vlan <vlan_id>`命令。 - H3C交换机:`interface <interface_id>`,然后使用`port access vlan <vlan_id>`命令。 3. 配置Trunk端口(用于连接不同交换机之间的端口): - Cisco交换机:`interface <interface_id>`,然后使用`switchport mode trunk`命令。 - H3C交换机:`interface <interface_id>`,然后使用`port trunk permit vlan all`命令。 4. 配置VLAN接口(用于三层交换): - Cisco交换机:`interface vlan <vlan_id>`,然后配置接口的IP地址和子网掩码。 - H3C交换机:`interface vlan-interface <vlan_id>`,然后配置接口的IP地址和子网掩码。 请注意,以上只是示例命令,具体的配置命令可能根据不同的交换机厂商和型号而有所不同。在进行VLAN配置时,请参考相应的交换机文档或与厂商支持团队联系以获取准确的配置指导。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值