1,springboot 定时任务方式:
-
一、基于注解(@Scheduled)
-
@Configuration //1.主要用于标记配置类,兼备Component的效果。 @EnableScheduling // 2.开启定时任务 public class SaticScheduleTask { //3.添加定时任务 @Scheduled(cron = "0/5 * * * * ?") //或直接指定时间间隔,例如:5秒 //@Scheduled(fixedRate=5000) private void configureTasks() { System.err.println("执行静态定时任务时间: " + LocalDateTime.now()); } }
-
二、基于接口(SchedulingConfigurer) 前者相信大家都很熟悉,但是实际使用中我们往往想从数据库中读取指定时间来动态执行定时任务,这时候基于接口的定时任务就派上用场了。
-
@Override public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) { scheduledTaskRegistrar.addTriggerTask( // 添加任务内容 () -> { //查询 new LambdaQueryWrapper<LimsServiceInspectPlan>().ne(LimsServiceInspectPlan::getIsInform,2) List<LimsServiceInspectPlan> plans = limsServiceInspectPlanService.list(); plans.forEach(inspectPlan -> { // 判断是否短期 Date pTime = inspectPlan.getStartTime(); // 开始时间 int verificationCycle = inspectPlan.getVerificationCycle();//执行周期 例如:没5天执行一次 int executionsNumber = inspectPlan.getExecutionsNumber();//执行次数 例如:执行5次 // 核查任务 - 开始时间 + 周期(天) = 到期时间, 到期后自动生成新的【质量核查任务】。 // 核查任务 - 执行次数 * 周期(天) + 到期时间 = 停止时间。 Date beginTime = DateUtils.getDateAfter(pTime,verificationCycle); // 生成质量核查任务时间 Date stopTime = DateUtils.getDateAfter(pTime,verificationCycle*executionsNumber); // 停止生成质量核查任务时间 Date today = DateUtils.getDate(); // 当前时间 Date endTime = DateUtils.getDateAfter(today,verificationCycle); // 生成质量核查任务时间 boolean after = today.before(stopTime); //当前时间在停止时间之前小于停止时间 说明可以继续执行 //Date1.after(Date2),当Date1大于Date2时,返回true,当小于等于时,返回false; //Date1.before(Date2),当Date1小于Date2时,返回true,当大于等于时,返回false; if(after) { // 生成质量的核查任务 LimsQualityTask qualityTask = new LimsQualityTask(); qualityTask.setTaskName(inspectPlan.getInspectPalnName()); qualityTask.setTaskType("HC"); qualityTask.setPlanStartTime(beginTime); qualityTask.setPlanEndTime(endTime); qualityTask.setResponsible(inspectPlan.getPerson()); qualityTask.setServiceTableId(inspectPlan.getId()+"");//关联能力核查计划ID limsQualityTaskService.save(qualityTask);//保存质量核查任务 //消息只通知一次, 通知过的排除 if (1==inspectPlan.getIsInform()) { // 这里用系统超级管理员admin为发送方 给所有责任人发信息 sysBaseAPI.sendSysAnnouncement("admin", inspectPlan.getPerson(), "服务能力"+inspectPlan.getInspectPalnName()+"核查任务提醒", "服务能力"+inspectPlan.getInspectPalnName()+"计划于"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(beginTime)+"至"+ inspectPlan.getInspectPalnName()+"计划于"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(endTime)+"进行能力核查,请及时处理!"); inspectPlan.setIsInform(2);//通知完后 修改通知状态为:已通知 limsServiceInspectPlanService.saveOrUpdate(inspectPlan); } } }); }, // 设置执行周期(Trigger) triggerContext -> { // 执行周期 log.info("能力服务-定时任务-开始执行"); // 每天凌晨1点11分执行定时任务,为了避免-你懂的 return new CronTrigger("0 11 1 * * ?").nextExecutionTime(triggerContext); } ); }
-
三、基于注解设定多线程定时任务
-
//@Component注解用于对那些比较中立的类进行注释; //相对与在持久层、业务层和控制层分别采用 @Repository、@Service 和 @Controller 对分层中的类进行注释 @Component @EnableScheduling // 1.开启定时任务 @EnableAsync // 2.开启多线程 public class MultithreadScheduleTask { @Async @Scheduled(fixedDelay = 1000) //间隔1秒 public void first() throws InterruptedException { System.out.println("第一个定时任务开始 : " + LocalDateTime.now().toLocalTime() + "\r\n线程 : " + Thread.currentThread().getName()); System.out.println(); Thread.sleep(1000 * 10); } @Async @Scheduled(fixedDelay = 2000) public void second() { System.out.println("第二个定时任务开始 : " + LocalDateTime.now().toLocalTime() + "\r\n线程 : " + Thread.currentThread().getName()); System.out.println(); } }
线程生命周期,及如何切换:
-
它的一个完整的生命周期中通常要经历如下的五种状态
新建(NEW): 当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态
可运行状态(RUNABLE): RUNNABLE状态可以认为包含两个子状态:READY和RUNNING,
就绪(READY): 处于新建状态的线程被start()后,将进入线程队列等待CPU时间片,此时它已具备了运行的条件,只是没分配到CPU资源 可能有CPU时间片
运行(RUNNING): 当就绪的线程被调度并获得CPU资源时便进入运行状态 有CPU时间片
阻塞(BLOCKED锁阻塞): 当一个线程试图获取一个对象锁来访问资源而该对象锁正被别的线程持有时,则该线程进入BLOCKED状态,直到该线程持有对象锁,该线程转为RUNABLE状态, 无CPU时间片
无限期等待(WAITING): 当一个线程在等待另一个线程执行一个动作(唤醒)时,该线程处于WAITING状态,该线程不能自动唤醒,必须等待其它线程显式执行唤醒方法notify、notifyAll等
有限期等待(TIMED_WAITING):无需等待被显式唤醒,到达设置期限后线程会被JVM自动唤醒
终结(TERMINATED): 线程完成了它的全部工作run方法正常结束或线程被提前强制性地中止或出现异常导致结束 -
线程在那些情况下回进入等待:
wait() sleep() join()
客户端和服务器之间最多能建立多少个连接:
只要资源 (内存硬盘cpu)足够,理论上可以接收无限个链接。
所谓65535的限制,是针对客户端的,客户机每链接一个服务,就必须开一个tcp端口与之对应。这样,链接到65535个服务后,本地端口就被占满。
服务端口仅仅使用一个。一个tcp链接,是四个元素决定的,server IP:port+client IP:port。
对于服务器,每一个tcp连接都要占一个文件描述符,一旦这个文件描述符使用完了,就会返回错误。
ArrayList 是线程不安全的
/*1.故障现象
* 报错java.util.ConcurrentModificationException
* 2.导致原因
* 并发争抢修改导致
* 3.解决方案
* new Vector();
* Collections.synchronizedList(new ArrayList<>());
* new CopyOnWriteArrayList<>();
* 4.优化建议
* 在读多写少的时候推荐使用 CopeOnWriteArrayList 这个类
*/
Set 是线程不安全的
-
//2种解决方案
-
//Set<String> set = Collections.synchronizedSet(new HashSet<>());
HashMap 是线程不安全的
-
//2种解决方案
-
// Map<String,String> map = Collections.synchronizedMap(new HashMap<>());
多线程中join和daemon的理解
1、join ()方法:主线程A中,创建了子线程B,并且在主线程A中调用了B.join(),那么,主线程A会在调用的地方等待,直到子线程B完成操作后,才可以接着往下执行,那么在调用这个线程时可以使用被调用线程的join方法。
2、setDaemon()方法。主线程A中,创建了子线程B,并且在主线程A中调用了B.setDaemon(),这个的意思是,把主线程A设置为守护线程,这时候,要是主线程A执行结束了,就不管子线程B是否完成,一并和主线程A退出.这就是setDaemon方法的含义,这基本和join是相反的。此外,还有个要特别注意的:必须在start() 方法调用之前设置,如果不设置为守护线程,程序会被无限挂起。
同步和异步,阻塞和非阻塞的区别:
Kafka优势及应用场景
一、Kafka的优势如下:
高吞吐量、低延迟:kafka每秒可以处理几十万条消息,它的延迟最低只有几毫秒;
可扩展性:kafka集群支持热扩展;
持久性、可靠性:消息被持久化到本地磁盘,并且支持数据备份防止数据丢失;
容错性:允许集群中节点故障(若副本数量为n,则允许n-1个节点故障);
高并发:支持数千个客户端同时读写。
二、Kafka适合以下应用场景:
日志收集:一个公司可以用Kafka可以收集各种服务的log,通过kafka以统一接口服务的方式开放给各种consumer;
消息系统:解耦生产者和消费者、缓存消息等;
用户活动跟踪:kafka经常被用来记录web用户或者app用户的各种活动,如浏览网页、搜索、点击等活动,这些活动信息被各个服务器发布到kafka的topic中,然后消费者通过订阅这些topic来做实时的监控分析,亦可保存到数据库;
运营指标:kafka也经常用来记录运营监控数据。包括收集各种分布式应用的数据,生产各种操作的集中反馈,比如报警和报告;
流式处理:比如spark streaming和storm。