-
Quartz 定时任务调度框架
2020-10-06 19:40:36定时任务调度框架 Quartz 入门案例: 依赖: <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.3.2</version> ...Quartz 定时任务调度框架
入门案例:
- 依赖:
<dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.3.2</version> </dependency> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz-jobs</artifactId> <version>2.3.2</version> </dependency>
- Job类:
package com.blu.job; import java.text.SimpleDateFormat; import java.util.Date; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; public class HelloJob implements Job{ @Override public void execute(JobExecutionContext context) throws JobExecutionException { Date date = new Date(); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String dateString = dateFormat.format(date); System.out.println("当前时间:"+dateString); } }
- 测试类:
package com.blu.test; import org.quartz.JobBuilder; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SimpleScheduleBuilder; import org.quartz.Trigger; import org.quartz.TriggerBuilder; import org.quartz.impl.StdSchedulerFactory; import com.blu.job.HelloJob; public class HelloScheduleTest { public static void main(String[] args) throws Exception { //调度器(Scheduler),从工厂中获取 Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); //任务示例(JobDetail) JobDetail jobDetail = JobBuilder.newJob(HelloJob.class).withIdentity("job1", "JobGroup1").build(); //触发器(Trigger) Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("trigger1", "TriggerGroup1") //立即启动触发器 .startNow() //每5s重复执行一次 .withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatSecondlyForever(5)) .build(); //让调度器关联任务和触发器 scheduler.scheduleJob(jobDetail,trigger); scheduler.start(); } }
- 运行效果:每5s打印一次时间
当前时间:2020-10-06 19:34:09 当前时间:2020-10-06 19:34:14 当前时间:2020-10-06 19:34:19 当前时间:2020-10-06 19:34:24 当前时间:2020-10-06 19:34:29 当前时间:2020-10-06 19:34:34 当前时间:2020-10-06 19:34:39 当前时间:2020-10-06 19:34:44 当前时间:2020-10-06 19:34:49 当前时间:2020-10-06 19:34:54 当前时间:2020-10-06 19:34:59 当前时间:2020-10-06 19:35:04
扩展
- Job类:
package com.blu.job; import java.text.SimpleDateFormat; import java.util.Date; import org.quartz.Job; import org.quartz.JobDataMap; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.quartz.JobKey; import org.quartz.Trigger; public class HelloJob implements Job{ private String message; public void setMessage(String message) { this.message = message; } @Override public void execute(JobExecutionContext context) throws JobExecutionException { Date date = new Date(); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String dateString = dateFormat.format(date); //获取JobDetail的内容 JobKey jobKey = context.getJobDetail().getKey(); System.out.println(jobKey.getName()); System.out.println(jobKey.getGroup()); System.out.println(context.getJobDetail().getJobClass().getName()); //从JobDetail对象中获取JobDataMap的数据 JobDataMap jobDataMap = context.getJobDetail().getJobDataMap(); String jobDataMessage = jobDataMap.getString("message"); System.out.println(jobDataMessage); //从Trigger对象中获取JobDataMap的数据 JobDataMap triggerDataMap = context.getTrigger().getJobDataMap(); String triggerDataMessage = triggerDataMap.getString("message"); System.out.println(triggerDataMessage); //获取Trigger中的内容 Trigger trigger = context.getTrigger(); System.out.println(trigger.getKey().getName()); System.out.println(trigger.getKey().getGroup()); System.out.println(trigger.getKey().getClass()); //初始化job类示例对象时会自动调用setter方法为属性赋值 //当trigger与JobDetail设置了重名属性时,setter方法获取的值就是trigger设置的属性值 System.out.println("通过setter直接获取数据:"+message); //获取其他内容 System.out.println("当前任务执行时间"+dateFormat.format(context.getFireTime())); System.out.println("下次任务的执行时间"+dateFormat.format(context.getNextFireTime())); System.out.println("当前时间:"+dateString); } }
- 测试类:
package com.blu.test; import org.quartz.JobBuilder; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SimpleScheduleBuilder; import org.quartz.Trigger; import org.quartz.TriggerBuilder; import org.quartz.impl.StdSchedulerFactory; import com.blu.job.HelloJob; public class HelloScheduleTest { public static void main(String[] args) throws Exception { //调度器(Scheduler),从工厂中获取 Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); //任务示例(JobDetail) JobDetail jobDetail = JobBuilder.newJob(HelloJob.class) .withIdentity("job1", "JobGroup1") .usingJobData("message","jobDetail传递的值") .build(); // System.out.println(jobDetail.getKey().getName()); // System.out.println(jobDetail.getKey().getGroup()); // System.out.println(jobDetail.getKey().getClass()); //触发器(Trigger) Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("trigger1", "TriggerGroup1") //立即启动触发器 .startNow() //每5s重复执行一次 .withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatSecondlyForever(5)) .usingJobData("message","trigger传递的值") .build(); //让调度器关联任务和触发器 scheduler.scheduleJob(jobDetail,trigger); scheduler.start(); } }
- 运行结果
job1 JobGroup1 com.blu.job.HelloJob jobDetail传递的值 trigger传递的值 trigger1 TriggerGroup1 class org.quartz.TriggerKey 通过setter直接获取数据:trigger传递的值 当前任务执行时间2020-10-08 13:52:33 下次任务的执行时间2020-10-08 13:52:38 当前时间:2020-10-08 13:52:33 job1 JobGroup1 com.blu.job.HelloJob jobDetail传递的值 trigger传递的值 trigger1 TriggerGroup1 class org.quartz.TriggerKey 通过setter直接获取数据:trigger传递的值 当前任务执行时间2020-10-08 13:52:38 下次任务的执行时间2020-10-08 13:52:43 当前时间:2020-10-08 13:52:38 job1 JobGroup1 com.blu.job.HelloJob jobDetail传递的值 trigger传递的值 trigger1 TriggerGroup1 class org.quartz.TriggerKey 通过setter直接获取数据:trigger传递的值 当前任务执行时间2020-10-08 13:52:43 下次任务的执行时间2020-10-08 13:52:48 当前时间:2020-10-08 13:52:43
@PersistJobDataAfterExecution 有状态的Job和无状态的Job:
每次执行任务时都会创建一个新的Job实例
默认的无状态Job每次调用时都会创建一个新的JobDataMap
而有状态的Job在多次Job调用期间可以在JobDataMap中存储一些状态信息
- Job类:
package com.blu.job; import java.text.SimpleDateFormat; import java.util.Date; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; @PersistJobDataAfterExecution public class HiJob implements Job{ private Integer count; public void setCount(Integer count) { this.count = count; } @Override public void execute(JobExecutionContext context) throws JobExecutionException { Date date = new Date(); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println("当前时间:"+ dateFormat.format(date)); //累加count,输出count,存入JobDataMap count++; System.out.println(count); context.getJobDetail().getJobDataMap().put("count", count); } }
- 测试类:
package com.blu.test; import org.quartz.JobBuilder; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SimpleScheduleBuilder; import org.quartz.Trigger; import org.quartz.TriggerBuilder; import org.quartz.impl.StdSchedulerFactory; import com.blu.job.HiJob; public class HiTest { public static void main(String[] args) throws Exception { Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); JobDetail jobDetail = JobBuilder.newJob(HiJob.class) .usingJobData("count",0) .build(); Trigger trigger = TriggerBuilder.newTrigger() .startNow() .withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatSecondlyForever(5)) .build(); scheduler.scheduleJob(jobDetail,trigger); scheduler.start(); } }
不加 @PersistJobDataAfterExecution 注解时是无状态Job,运行效果:
当前时间:2020-10-09 11:03:53 1 当前时间:2020-10-09 11:03:58 1 当前时间:2020-10-09 11:04:03 1 当前时间:2020-10-09 11:04:08 1 当前时间:2020-10-09 11:04:13 1 当前时间:2020-10-09 11:04:18 1
加上 @PersistJobDataAfterExecution 注解后是有状态Job,运行效果:
当前时间:2020-10-09 11:06:25 1 当前时间:2020-10-09 11:06:30 2 当前时间:2020-10-09 11:06:35 3 当前时间:2020-10-09 11:06:40 4 当前时间:2020-10-09 11:06:45 5 当前时间:2020-10-09 11:06:50 6
其他注解:
@DisallowConcurrentExecution
默认的情况下,无论上一次任务是否结束或者完成,只要规定的时间到了,那么下一次就开始。
使用 @DisallowConcurrentExecution 注解可以保证上一次任务成功结束后,下一次任务才能开始
-
任务调度
2016-06-12 15:13:54任务调度的使用 crontab 1、设置任务调度文件 crontab -e 2、每隔一定时间去执行任务 希望每天凌晨两点半去执行date >>/temp.txt,如下 crontab -e ,加入如下代码: 0 2 * * * date >>/home/mydata...任务调度的使用 crontab
1、设置任务调度文件
crontab -e
2、每隔一定时间去执行任务
希望每天凌晨两点半去执行date >>/temp.txt,如下
crontab -e ,加入如下代码:
0 2 * * * date >>/home/mydata2
* * * * * 表示分钟,小时,几号,月,星期
如果希望每分钟执行
* * * * * date >>/home/mydata2
全部为*crontab 最高精确到分
调度文件的规则:分钟,小时,几号,月,星期 命令名称
如果月设为13,这时系统不会执行,也不会报错
3、如何去调度多个任务
1)在crontab -e 中直接写
缺点:太多,太乱,不好管理,没有系统化
2)可以把所有的任务,写入到一个可执行文件(SHELL 编程),类似于windows批处理状态
建议用这个方法
vi mytask.sh 写入文件,date >>/temp.txt
.sh 表示shell
chmod 修改权限 必须要有X权限
crontab -e 进入,* * * * * /root/mytask.sh
可以把多个文件放到里面
4、如何终止任务
crontab -r 终止任务 删除里面的内容
crontab -l 列出当前有哪些任务调度 -
-
抢占式任务调度和非抢占式(轮询任务调度)的区别,以及任务调度算法的用途。
2019-02-18 10:26:521、说说轮巡任务调度与抢占式任务调度的区别? 答:轮询任务调度与抢占式任务调度的区别在于抢占式调度可以因为优先级高的任务抢占cpu,而轮询的不能。 2当软件线程个数超过硬件线程个数的时候,支持抢占式多任务...1、说说轮巡任务调度与抢占式任务调度的区别?
答:轮询任务调度与抢占式任务调度的区别在于抢占式调度可以因为优先级高的任务抢占cpu,而轮询的不能。
2当软件线程个数超过硬件线程个数的时候,支持抢占式多任务处理的操作系统一般会采用时间片轮转调度的方案。
3 对于RTOS中,理解这两个概念是很重要的。实时系统对于响应时间是有非常严格的要求,尤其是在硬实时系统中,没有满足响应时间的上限将视为系统失败。影响RTOS响应时间的一个重要方面就是任务调度算法。在 RTOS中,主要的调度算法是基于任务优先级的抢占式调度。在这种调度算法中,系统总是选择优先级别的最高的算法进行调度,并且 一旦高优先级别的任务准备就绪之后,它就会马上被调度而不等待低优先级的任务主动放弃CPU。这和通用OS的时间片轮转调度算法是 不一样的,在时间片轮转调度算法中,只有等任务主动放弃CPU,高优先级的任务才有调度的优先权。在基于任务优先级抢占式调度算法中,会产生一个优先级反转问题。解决这个问题的方式主要包括继承优先级和天花板策略。继承优先级策略是一旦高优先级的任务所需要的竞争资源被低优先级的任务使用,就提高低优先级的任务的优先级别,从而使得能使竞争资源尽快释放。天花板策略是在创建信号量的时候,根据可能使用该信号量所有的任务的最高优先级别来设置当前任务的优先级,这个优先级是由创建资源的用户来决定。
4很多编程人员都认为,使用多线程能够提升程序的性能,如果少量的线程能够提升程序的性能,他们就会认为更多的线程能够更好。但实际上,多线程只是为不同的程序比较合理地安排运行时间,更加充分的利用系统资源,这当中存在着一个线程数和程序性能的平衡,过多的线程可能会严重影响程序的性能。这种影响主要有以下两个方面:首先,将给定的工作量划分给过多的线程会造成每个线程的工作量过少,因此可能导致线程启动和终止时的开销比程序实际工作的开销还要多;其次,过多并发线程的存在将导致共享有限硬件资源的开销增大。
操作系统使用进程将它们正在执行的不同应用程序分开。线程是操作系统分配处理器时间的基本单元,并且进程中可以有多个线程同时执行代码。每个线程都维护异常处理程序、调度优先级和一组系统用于在调度该线程前保存线程上下文的结构。线程上下文包括为使线程在线程的宿主进程地址空间中无缝地继续执行所需的所有信息,包括线程的 CPU 寄存器组和堆栈。
当软件线程个数超过硬件线程个数的时候,支持抢占式多任务处理的操作系统一般会采用时间片轮转调度的方案。它通过以下方式实现这一点:在需要硬件线程时间的软件线程之间分割可用硬件线程时间,并轮流为每个线程分配硬件线程时间片(time slice)。当前执行的软件线程在其时间片结束时被挂起,而另一个软件线程继续运行。当系统从一个软件线程切换到另一个软件线程时,它将保存被抢占的软件线程的线程上下文,并重新加载线程队列中下一个软件线程的已保存线程上下文。
时间片的长度取决于操作系统和处理器。由于每个时间片都很小,因此即使只有一个处理器,多个线程看起来似乎也是在同时执行。这实际上就是多处理器系统中发生的情形,在此类系统中,可执行线程分布在多个可用处理器中。
时间片机制保证了所有的软件线程都能够被执行,这样也就避免了某些软件线程长期占据硬件线程,而导致其他的软件线程出现饥饿(starvation)的情况。但是,操作系统的时间片轮转调度方案也将引入额外的开销,随着软件线程的增多,这种开销将会急剧增加,进而降低系统性能。这种开销主要有以下几种:
首先是线程间切换时保存和恢复进程寄存器的开销。在系统要进行线程间切换的时候,需要保存当前线程的寄存器状态,以便在下次回调到当前线程的时候,能够恢复线程寄存器继续运行。当存在少量线程的时候,进程调度程序会给每一个线程分配足够长的时间片,相比较之下,保存和恢复线程寄存器的开销变得不是很显著。但是随着线程数目的增加,线程调度程序分给每个线程的时间片也会相应减少,而保存和恢复线程寄存器的开销不变,这样,在每个时间片当中,系统将更多的时间用于保存和恢复线程寄存器,就会显著地降低系统性能。
另一方面,在使用时间片机制的时候,保存和恢复线程使用的cache的开销则是更敏感的一种开销。现代体系结构中,cache通常比主存快10到100倍,CPU访问cache并命中对于系统性能的提高具有非常重要的作用。而当存在线程切换的时候,新的线程要访问的数据尚未装入cache,CPU需要从主存中读取信息的同时,cache替换策略把该地址所在的那块存储内容从主存拷贝到cache中。一般情况下,cache替换策略是使用最近最少使用策略(LRU)选择的,LRU策略是把当前近期cache中使用次数最少的那块数据块替换出去,而这些数据块中的数据很可能就是上几个时间片的线程使用的。这样,各个线程就在不断地竞争cache,相互淘汰彼此使用的cache数据,不断造成cache扑空,最终降低了系统性能。
此外,在更底层的存储器结构上也存在相似的问题,那就是线程对主存的争夺。现代大部分操作系统都实现了虚拟内存。一台机器上同时运行着很多程序,这些程序所需的存储空间可能比存储器的实际容量要大得多,但是在每个时间点,存储器只有一部分被激活。主存只需存放众多程序中的活跃部分,就像cache中只存放一个程序的活跃部分一样,而其他的部分则存储到磁盘上。由于每个线程都有自己的栈空间和私有数据结构。类似于对cache的争夺,随着线程数的增加,线程之间就会争夺主存,导致性能上的损失。
以上几种问题是由于线程对共享资源的争夺产生的,另外还存在一个性质不同,但是后果可能更加严重的问题,称为护航(convoying),它是指线程聚集在一起,等待获取某一个锁。当某个线程持有一个锁的时候,用完了自己的时间片,这时所有等待锁的线程都必须等待这个线程被唤醒并且释放锁。
最好的解决方法是依据实际情况,使用尽可能少的线程,这样可以最大限度地减少操作系统资源的使用,并可提高性能。千万不要硬性规定线程的个数,而是将其作为一个可调节的参数。
操作系统中存在两种类型的线程,I/O阻塞线程和计算非阻塞线程。许多应用程序,例如终端机模拟程序,都需要在同一时间处理对一个以上的文件的读写操作,我们不可能依次地等待每个请求完成才继续处理下一个请求,而是让所有这些I/O操作并行处理,并且当任何一个I/O完成时,应用程序会收到一个事件通告,这种处理I/O的线程称为I/O阻塞线程。而另外一些进程,例如进行大量计算,处理图形的线程则称为计算非阻塞线程。I/O阻塞线程不会引起时间片切换开销,而计算非阻塞线程则会引起时间片切换的开销。所以,将I/O阻塞线程和计算非阻塞线程分离开是一种非常好的组织方法。计算非阻塞线程在大多数时间内都是被调度函数调度到的,应该和处理器资源相匹配,而I/O阻塞线程在大多数时间内都在等待外部事件。
由于构造良好的多线程应用程序要多方面地考虑资源要求和潜在冲突,需要相当的专业技术,所以一般最好采用现成的软件来完成,下面的一些经验是很有帮助的:
建议您尽量使用OpenMP。OpenMP提供了一种简单的方法,供程序员描述要并行的循环迭代,而不用创建具体数目的线程,OpenMP可以根据目标系统尽量使用最优数量的线程个数。
建议您使用线程池。每个传入的请求都将分配给线程池中的一个线程,因此可以异步处理请求,而不会占用主线程,也不会延迟后续请求的处理。一旦池中的某个线程完成任务,它将返回到等待线程队列中,等待被再次使用,这种重用使应用程序可以避免为每个线程创建新进程的开销。线程池通常具有最大线程数限制,如果所有线程都繁忙,而额外的任务将放入队列中,直到有线程可用时才能够得到处理。当您不需要给一个任务设定特定的优先级,当不会有可能会运行很长时间的任务(并因此阻塞了其他任务),我们建议您使用线程池,技术高超的软件设计专家可能希望编写自己的任务调度程序,一般都采用任务窃取(work stealing)的方法,它是指每个线程都有自己私有的任务集合,当一个线程执行完自己的任务以后,它就从其他线程的任务集合中窃取任务来执行。任务窃取实现了较好的cache利用率和负载平衡。当一个线程运行自己的任务时,它往往会重用自己cache中的数据,当它完成自己的任务,就需要窃取任务来运行,这实际上就是一种负载平衡。高效的任务策略在于窃取目标的选择,选择较大的任务能够使窃取者忙碌相当长一段时间。早期的Clik调度程序(Blumofe,1995年)就是一个关于如何编写高效的任务窃取调度程序的范例。 -
轮询任务调度与抢占式任务调度
2016-09-20 12:19:081、说说轮巡任务调度与抢占式任务调度的区别? 答:轮询任务调度与抢占式任务调度的区别在于抢占式调度可以因为优先级高的任务抢占cpu,而轮询的不能。 2当软件线程个数超过硬件线程个数的时候,支持抢占式多...1、说说轮巡任务调度与抢占式任务调度的区别?
答:轮询任务调度与抢占式任务调度的区别在于抢占式调度可以因为优先级高的任务抢占cpu,而轮询的不能。
2当软件线程个数超过硬件线程个数的时候,支持抢占式多任务处理的操作系统一般会采用时间片轮转调度的方案。
3 对于RTOS中,理解这两个概念是很重要的。实时系统对于响应时间是有非常严格的要求,尤其是在硬实时系统中,没有满足响应时间的上限将视为系统失败。影响RTOS响应时间的一个重要方面就是任务调度算法。在 RTOS中,主要的调度算法是基于任务优先级的抢占式调度。在这种调度算法中,系统总是选择优先级别的最高的算法进行调度,并且 一旦高优先级别的任务准备就绪之后,它就会马上被调度而不等待低优先级的任务主动放弃CPU。这和通用OS的时间片轮转调度算法是 不一样的,在时间片轮转调度算法中,只有等任务主动放弃CPU,高优先级的任务才有调度的优先权。在基于任务优先级抢占式调度算法中,会产生一个优先级反转问题。解决这个问题的方式主要包括继承优先级和天花板策略。继承优先级策略是一旦高优先级的任务所需要的竞争资源被低优先级的任务使用,就提高低优先级的任务的优先级别,从而使得能使竞争资源尽快释放。天花板策略是在创建信号量的时候,根据可能使用该信号量所有的任务的最高优先级别来设置当前任务的优先级,这个优先级是由创建资源的用户来决定。
4很多编程人员都认为,使用多线程能够提升程序的性能,如果少量的线程能够提升程序的性能,他们就会认为更多的线程能够更好。但实际上,多线程只是为不同的程序比较合理地安排运行时间,更加充分的利用系统资源,这当中存在着一个线程数和程序性能的平衡,过多的线程可能会严重影响程序的性能。这种影响主要有以下两个方面:首先,将给定的工作量划分给过多的线程会造成每个线程的工作量过少,因此可能导致线程启动和终止时的开销比程序实际工作的开销还要多;其次,过多并发线程的存在将导致共享有限硬件资源的开销增大。
操作系统使用进程将它们正在执行的不同应用程序分开。线程是操作系统分配处理器时间的基本单元,并且进程中可以有多个线程同时执行代码。每个线程都维护异常处理程序、调度优先级和一组系统用于在调度该线程前保存线程上下文的结构。线程上下文包括为使线程在线程的宿主进程地址空间中无缝地继续执行所需的所有信息,包括线程的 CPU 寄存器组和堆栈。
当软件线程个数超过硬件线程个数的时候,支持抢占式多任务处理的操作系统一般会采用时间片轮转调度的方案。它通过以下方式实现这一点:在需要硬件线程时间的软件线程之间分割可用硬件线程时间,并轮流为每个线程分配硬件线程时间片(time slice)。当前执行的软件线程在其时间片结束时被挂起,而另一个软件线程继续运行。当系统从一个软件线程切换到另一个软件线程时,它将保存被抢占的软件线程的线程上下文,并重新加载线程队列中下一个软件线程的已保存线程上下文。
时间片的长度取决于操作系统和处理器。由于每个时间片都很小,因此即使只有一个处理器,多个线程看起来似乎也是在同时执行。这实际上就是多处理器系统中发生的情形,在此类系统中,可执行线程分布在多个可用处理器中。
时间片机制保证了所有的软件线程都能够被执行,这样也就避免了某些软件线程长期占据硬件线程,而导致其他的软件线程出现饥饿(starvation)的情况。但是,操作系统的时间片轮转调度方案也将引入额外的开销,随着软件线程的增多,这种开销将会急剧增加,进而降低系统性能。这种开销主要有以下几种:
首先是线程间切换时保存和恢复进程寄存器的开销。在系统要进行线程间切换的时候,需要保存当前线程的寄存器状态,以便在下次回调到当前线程的时候,能够恢复线程寄存器继续运行。当存在少量线程的时候,进程调度程序会给每一个线程分配足够长的时间片,相比较之下,保存和恢复线程寄存器的开销变得不是很显著。但是随着线程数目的增加,线程调度程序分给每个线程的时间片也会相应减少,而保存和恢复线程寄存器的开销不变,这样,在每个时间片当中,系统将更多的时间用于保存和恢复线程寄存器,就会显著地降低系统性能。
另一方面,在使用时间片机制的时候,保存和恢复线程使用的cache的开销则是更敏感的一种开销。现代体系结构中,cache通常比主存快10到100倍,CPU访问cache并命中对于系统性能的提高具有非常重要的作用。而当存在线程切换的时候,新的线程要访问的数据尚未装入cache,CPU需要从主存中读取信息的同时,cache替换策略把该地址所在的那块存储内容从主存拷贝到cache中。一般情况下,cache替换策略是使用最近最少使用策略(LRU)选择的,LRU策略是把当前近期cache中使用次数最少的那块数据块替换出去,而这些数据块中的数据很可能就是上几个时间片的线程使用的。这样,各个线程就在不断地竞争cache,相互淘汰彼此使用的cache数据,不断造成cache扑空,最终降低了系统性能。
此外,在更底层的存储器结构上也存在相似的问题,那就是线程对主存的争夺。现代大部分操作系统都实现了虚拟内存。一台机器上同时运行着很多程序,这些程序所需的存储空间可能比存储器的实际容量要大得多,但是在每个时间点,存储器只有一部分被激活。主存只需存放众多程序中的活跃部分,就像cache中只存放一个程序的活跃部分一样,而其他的部分则存储到磁盘上。由于每个线程都有自己的栈空间和私有数据结构。类似于对cache的争夺,随着线程数的增加,线程之间就会争夺主存,导致性能上的损失。
以上几种问题是由于线程对共享资源的争夺产生的,另外还存在一个性质不同,但是后果可能更加严重的问题,称为护航(convoying),它是指线程聚集在一起,等待获取某一个锁。当某个线程持有一个锁的时候,用完了自己的时间片,这时所有等待锁的线程都必须等待这个线程被唤醒并且释放锁。
最好的解决方法是依据实际情况,使用尽可能少的线程,这样可以最大限度地减少操作系统资源的使用,并可提高性能。千万不要硬性规定线程的个数,而是将其作为一个可调节的参数。
操作系统中存在两种类型的线程,I/O阻塞线程和计算非阻塞线程。许多应用程序,例如终端机模拟程序,都需要在同一时间处理对一个以上的文件的读写操作,我们不可能依次地等待每个请求完成才继续处理下一个请求,而是让所有这些I/O操作并行处理,并且当任何一个I/O完成时,应用程序会收到一个事件通告,这种处理I/O的线程称为I/O阻塞线程。而另外一些进程,例如进行大量计算,处理图形的线程则称为计算非阻塞线程。I/O阻塞线程不会引起时间片切换开销,而计算非阻塞线程则会引起时间片切换的开销。所以,将I/O阻塞线程和计算非阻塞线程分离开是一种非常好的组织方法。计算非阻塞线程在大多数时间内都是被调度函数调度到的,应该和处理器资源相匹配,而I/O阻塞线程在大多数时间内都在等待外部事件。
由于构造良好的多线程应用程序要多方面地考虑资源要求和潜在冲突,需要相当的专业技术,所以一般最好采用现成的软件来完成,下面的一些经验是很有帮助的:
建议您尽量使用OpenMP。OpenMP提供了一种简单的方法,供程序员描述要并行的循环迭代,而不用创建具体数目的线程,OpenMP可以根据目标系统尽量使用最优数量的线程个数。
建议您使用线程池。每个传入的请求都将分配给线程池中的一个线程,因此可以异步处理请求,而不会占用主线程,也不会延迟后续请求的处理。一旦池中的某个线程完成任务,它将返回到等待线程队列中,等待被再次使用,这种重用使应用程序可以避免为每个线程创建新进程的开销。线程池通常具有最大线程数限制,如果所有线程都繁忙,而额外的任务将放入队列中,直到有线程可用时才能够得到处理。当您不需要给一个任务设定特定的优先级,当不会有可能会运行很长时间的任务(并因此阻塞了其他任务),我们建议您使用线程池,技术高超的软件设计专家可能希望编写自己的任务调度程序,一般都采用任务窃取(work stealing)的方法,它是指每个线程都有自己私有的任务集合,当一个线程执行完自己的任务以后,它就从其他线程的任务集合中窃取任务来执行。任务窃取实现了较好的cache利用率和负载平衡。当一个线程运行自己的任务时,它往往会重用自己cache中的数据,当它完成自己的任务,就需要窃取任务来运行,这实际上就是一种负载平衡。高效的任务策略在于窃取目标的选择,选择较大的任务能够使窃取者忙碌相当长一段时间。早期的Clik调度程序(Blumofe,1995年)就是一个关于如何编写高效的任务窃取调度程序的范例。 -
任务调度(定时任务)
2020-01-28 22:31:07任务调度、Java常见的任务调度方式、任务调度的问题、分布式任务调度平台 -
任务调度中心
2019-10-22 14:58:54因工作需要,需要构建独立于业务系统之外的任务调度中心,解决当前业务系统的痛点,将调度任务做成可配置化、动态化、方便监控管理的调度中心。 xxl-job 1.9版本系统架构图如下,主要分为两大模块: 一、调度中心 ... -
hera(赫拉)任务调度系统--为数据平台打造的任务调度系统
2019-05-08 14:46:07hera(赫拉)任务调度系统–为数据平台打造的任务调度系统 hera项目背景 在大数据部门,随着业务发展,每天承载着成千上万的ETL任务调度,这些任务集中在hive,shell脚本调度。怎么样让大量的ETL任务准确的完成... -
ETL任务调度
2020-06-16 15:25:45而ETL任务调度(简称ETL调度)用于控制ETL任务的启动运行(启动时间、运行周期及触发条件),实现数据的传输转换操作。 ETL调度按照功能复杂度分为简单定时调度和工作流调度二种方式。 定时调度用于控制ETL任务... -
轮询任务调度和抢占式任务调度有什么区别
2017-08-05 11:29:54轮询任务调度与抢占式任务调度的区别在于抢占式调度中的优先级可以抢占CPU,而轮询的不能。具体而言,轮询调度的原理是每一次把来自用户的请求轮流的分配给内部服务器,从1开始,直到N(内部服务器的个数),然 -
【任务调度系统第一篇】:大数据任务调度框架
2018-12-01 17:22:58任务调度系统在大数据平台架构中扮演着比较重要的角色。下图是引自网易的猛犸大数据平台lambda架构图。 其中的Azkaban就是其任务调度组件。概括来说,任务调度在大数据平台中所扮演的角色主要有: 任务编排... -
UCOSIII的任务调度 UCOSII任务是怎么调度的
2019-04-18 21:26:02UCOS-III的任务调度 任务调度就是CPU终止当前正在运行的任务,转而去执行其他就绪的任务。...UCOS-III的任务调度是有任务调度器来完成的(所谓任务调度器其实就是两个函数),任务调度器函数有2种:分别是 任务级... -
FreeRTOS 任务调度 任务切换
2016-10-15 17:07:34@(嵌入式) 简述 ...一般来说, 我们会在程序开始先创建若干个任务, 而此时任务调度器还没又开始运行,因此每一次任务创建后都会依据其优先级插入到就绪链表,同时保证全局变量 pxCurrentTCB 指向当 -
UCOSIII任务调度和任务切换
2018-12-04 20:15:461、UCOSIII任务调度 1.1、可剥夺型任务调度: 任务调度就是中止当前正在运行的任务转而去执行其他的任务。 UCOSIII是可剥夺型内核,因此当一个高优先级的任务准备就绪,并且此时发生了任务调度,那么这个高优先级... -
任务调度框架Quartz(一) Quartz——一个强大的定时任务调度框架
2016-11-04 00:00:05Quartz,水晶、石英,一个简单朴素有美丽的名字,在Java程序界,Quartz大名鼎鼎,很多Java应用几乎都集成或构建了一个定时任务调度系统,Quartz是一个定时任务调度框架。 何为定时任务调度框架?简而言之,它可以... -
任务调度--spring下的任务调度quartz
2015-09-13 09:14:38之前写过Timer实现任务调度,这篇文章用来写一下在spring下使用quartz实现任务调度,直接上代码: 定义任务对象:package com; /** * 1. 定义任务对象 * * @author Administrator * */ public class Data... -
微服务任务调度框架
2020-01-20 23:24:42sia-task: 框架组成部分: 1、任务执行器2、任务调度中心3、任务编排中心4、任务注册中心(zookeeper)5、持久存储...3、任务调度中心从持久化存储获取调度信息 4、任务调度中心按照调度逻辑访问任务执行器 ... -
工作流任务调度系统:Apache DolphinScheduler
2019-10-28 16:21:51Apache DolphinScheduler(目前处在孵化阶段,原名为EasyScheduler)是一个分布式、去中心化、易扩展的可视化DAG工作流任务调度系统,其致力于解决数据处理流程中错综复杂的依赖关系,使调度系统在数据处理流程中开... -
Spark的任务调度
2018-11-03 15:01:391、学习任务调度前需要了解的知识点 1.1、Spark中的一些专业术语 1.1.1、任务相关 1.1.2、资源相关 1.2、RDD中的依赖关系 1.2.1、宽依赖 1.2.2、窄依赖 1.2.3、宽窄依赖的作用 1.3、学习任务调度前... -
Spark资源调度和任务调度
2019-05-04 22:32:26Spark资源调度和任务调度的流程: 启动集群后,Worker节点会向Master节点汇报资源情况,Master掌握了集群资源情况。当Spark提交一个Application后,根据RDD之间的依赖关系将Application形成一个DAG有向无环图。任务... -
分布式任务调度平台
2018-04-07 12:23:36一、任务调度概述 1.什么是任务调度--定时job 在什么时间进行执行代码任务任务调度场景:定时检查红包过期2.java实现定时任务有几种? Thread.sleep TimerTask util包下 线程池,可定时线程 Quartz定时任务调度... -
分布式任务调度框架
2020-09-04 16:44:52task是一个任务调度统一管理平台。 目前主要是通过http来进行任务的调度,http支持签名算法。 一张图能更加懂它是做什么的(一个集中管理任务的平台) [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来... -
Spark 任务调度
2018-03-06 19:07:03Spark 任务调度1. 任务调度流程图各个RDD之间存在着依赖关系,这些依赖关系就形成有向无环图DAG,DAGScheduler对这些依赖关系形成的DAG进行Stage划分,划分的规则很简单,从后往前回溯,遇到窄依赖加入本stage,遇见... -
Linux 任务调度
2019-05-07 16:10:21任务调度就是指系统在某个时间执行特定的命令或程序。 分类: 系统工作:有些重要的工作必须周而复始的执行,如病毒扫描 个别用户工作:用户想要执行某些程序,如mysql数据库的备份 指令:crontab 基本语法: ...
-
节能技术服务认证、综合能源服务认证的背景
-
2021-02-25
-
1.2 从零开始学Go语言:编写第一个Go程序
-
SAM模式:构建函数响应式前端架构过程中学到的经验
-
物联网基础篇:快速玩转MQTT
-
MHA 高可用 MySQL 架构与 Altas 读写分离
-
只有你想不到!产品推广的108种方法!
-
OTL用于多目标优化的比较研究
-
Galera 高可用 MySQL 集群(PXC v5.7+Hapro)
-
ubuntu下 vi输入方向键会变成ABCD,
-
redis常见面试题
-
MyApplication190.zip
-
Android自定义控件(五)---实战篇(优化篇)
-
RFC-3550-中文版.zip
-
Amoeba 实现 MySQL 高可用、负载均衡和读写分离
-
激光等离子体X射线偏振度探测
-
项目代码demo(web+api)
-
Linux基础入门系列课程
-
选中后样式
-
Y470_Y570 2.12 bios 64位