当前位置: 首页 > 后端技术 > Java

原定取消时间活动

时间:2023-04-01 17:00:43 Java

1.定时任务/**定时任务优点:简单易实现,支持集群运行缺点:(1)消耗服务器内存大(2)有延迟,比如你3分钟扫描一次,延迟最差时间为3分钟(3)数据量大,每隔几分钟扫描一次数据库。*/publicclassMyJobimplementsJob{publicvoidexecute(JobExecutionContextjobExecutionContext)throwsJobExecutionException{System.out.println("进入数据库");}publicstaticvoidmain(String[]args)throwsSchedulerException{//创建作业JobDetailjobDetail=JobBuilder.newJob(MyJob.class).withIdentity("job1","group1").build();//创建一个触发器Trigger每三秒执行一次Triggertrigger=TriggerBuilder.newTrigger().withIdentity("trigger1","group3").withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(3).repeatForever()).build();/**创建并初始化QuartzScheduler调度工厂调用getScheduler()在工厂中会生成调度器*/Schedulerscheduler=newStdSchedulerFactory().getScheduler();//组合任务及其触发器放入调度器scheduler.scheduleJob(jobDetail,trigger);//调度器开始调度任务scheduler.start();}}2.延迟队列/**延迟队列是JDK自带的DelayQueue实现的,是一个无界阻塞只有在延迟到期时才能从中获取元素的队列,放入DelayQueue的对象必须实现Delayed接口的优点:效率高,任务触发时延低。缺点:(1)服务器重启后所有数据都会消失,怕宕机(2)集群扩容比较麻烦(3)由于内存条件限制,比如未支付的订单太多,会OOM容易出现异常(4)代码复杂度高*/publicclassOrderDelayimplementsDelayed{privateStringorderId;privateLongtimeout;OrderDelay(StringorderId,Longtimeout){this.orderId=orderId;this.timeout=timeout+System.nanoTime();}/**用于延迟队列内部比较排序当前时间的延迟时间-比较对象的延迟时间@paramother@return*/publicintcompareTo(Delayedother){if(other==this){return0;}OrderDelayt=(OrderDelay)other;Longd=(getDelay(TimeUnit.NANOSECONDS)-t.getDelay(TimeUnit.NANOSECONDS));返回(d==0)?0:((d<0)?-1:1);}/**返回距离你自定义的超时时间还有多长时间得到延迟时间到期时间-当前时间@paramunit@return*/publiclonggetDelay(TimeUnitunit){returnunit.convert(timeout-System.nanoTime(),TimeUnit.NANOSECONDS);}voidprint(){System.out.println(orderId+"started");}}3、时间轮算法包wheeltime;importio.netty.util.*;importio.netty.util.TimerTask;importjava.util.concurrent.*;/**Netty的HashedWheelTimer来了实现

的优点:效率高,任务触发时延比delayQueue低,代码复杂度比delayQueue低缺点:(1)服务器重启后所有数据消失,怕宕机(2)集群扩容比较麻烦(3)由于内存条件的限制,比如未支付订单过多,很容易出现OOM异常*/publicclassHashedWheelTimerTest{staticclassMyTimerTaskimplementsTimerTask{booleanflag;publicMyTimerTask(booleanflag){this.flag=flag;}publicvoidrun(Timeouttimeout)throwsException{System.out.println("要删除...");this.flag=false;}}publicstaticvoidmain(String[]argv){MyTimerTasktimerTask=newMyTimerTask(true);Timertimer=newHashedWheelTimer();//轮数,时间,时间单位定时器.newTimeout(timerTask,5,TimeUnit.SECONDS);inti=1;while(timerTask.flag){try{Thread.sleep(1000);}catch(InterruptedExceptione){e.printStackTrace();}System.out.println(i+"past");i++;}}}4.redis缓存方法一:使用rediszset,zset是一个有序集合,每个元素(成员)关联一个score,按score排序取值在collection和使用redis命令理解思路添加单个元素redis>ZADDpage_rank10google.com(integer)1添加多个元素redis>ZADDpage_rank9baidu.com8bing.com(integer)2redis>ZRANGEpage_rank0-1WITHSCORES1)"bing.com"2)"8"3)"baidu.com"4)"9"5)"google.com"6)"10"查询元素的分值redis>ZSCOREpage_rankbing.com"8"去除单个元素redis>ZREMpage_rankgoogle.com(integer)1redis>ZRANGEpage_rank0-1WITHSCORES1)"bing.com"2)"8"3)"baidu.com"4)"9"java实现:packagetimestop.redisout;importredis.clients.jedis.*;importjava.util.*;/**使用redis缓存的缺点:高并发情况下,多个消费者会fetchImprovements对于同一个订单号:(1)使用分布式锁,但是有了分布式锁,性能下降,这个解决方案不详述。(2)判断ZREM的返回值,大于0才消费数据。so*/publicclassAppTest{privatestaticfinalStringADDR="127.0.0.1";privatestaticfinalintPORT=6379;//consumerDelayMessage()方法中的JedisPool创建线程安全的网络连接池privatestaticJedisPoolJedisPool=newJedisPool(ADDR,PORT);//获取连接池的一个jedis对象publicstaticJedisgetJedis(){returnJedisPool.getResource();}//Producer,生成5条订单放入publicvoidproductionDelayMessage(){for(inti=0;i<5;i++){/***设置延时3秒*Calendar.getInstance()*取一个Calendar对象并计算时间并指定时区*/Calendarcal1=Calendar.getInstance();//Calendar.SECOND时间单位,金额时间cal1.add(Calendar.SECOND,3);//cal1.getTimeInMillis()用于返回本日历的当前时间(以毫秒为单位)。intsecond3later=(int)(cal1.getTimeInMillis()/1000);//getJedis().zadd添加元素AppTest.getJedis().zadd("OrderId",second3later,"OID0000001"+i);系统输出。println(System.currentTimeMillis()+"ms:redis已经生成订单任务:订单ID为\"+\"OID0000001"+i);}}//消费者,接单publicvoidconsumerDelayMessage(){Jedisjedis=AppTest.getJedis();while(true){//返回有序集合中指定分数区间的成员列表。Set<元组>items=jedis.zrangeWithScores("OrderId",0,1);if(items==null||items.isEmpty()){System.out.println("当前没有等待任务");尝试{Thread.sleep(500);}catch(InterruptedExceptione){e.printStackTrace();}继续;}intscore=(int)((Tuple)items.toArray()[0]).getScore();/***Calendar.getInstance()*取一个Calendar对象并计算时间并指定时区*/Calendarcal=Calendar.getInstance();//cal1.getTimeInMillis()用于返回本日历的当前时间(以毫秒为单位)。intnowSecond=(int)(cal.getTimeInMillis()/1000);//原始版本有多个线程消耗相同的资源//if(nowSecond>=score){//StringorderId=((Tuple)items.toArray()[0]).getElement();//jedis.zrem("OrderId",orderId);//System.out.println(System.currentTimeMillis()+"ms:redis消费了一个任务:消费的OrderId为"+orderId);//}//改进if(nowSecond>=score){StringorderId=((Tuple)items.toArray()[0]).getElement();Longnum=jedis.zrem("OrderId",orderId);if(num!=null&&num>0){System.out.println(System.currentTimeMillis()+"ms:redis消费一个任务:消费订单的OrderId为"+orderId);}}}}publicstaticvoidmain(String[]args){AppTestappTest=newAppTest();appTest.productionDelayMessage();appTest.consumerDelayMessage();}}方法二:本方案使用redis的KeyspaceNotifications,中文翻译就是keyspace机制,即就是利用这个机制在key过期后提供回调。其实redis会向客户端发送消息,要求redis2.8以上版本。实践二在redis.conf中,加入一条配置notify-keyspace-eventsExpublicclassRedisTest{privatestaticfinalStringADDR="127.0.0.1";privatestaticfinalintPORT=6379;privatestaticJedisPooljedis=newJedisPool(ADDR,PORT);privatestaticRedisSubsub=newRedisSub();publicstaticvoidinit(){newThread(newRunnable(){publicvoidrun(){jedis.getResource().subscribe(sub,"__keyevent@0__:expired");}}).start();}publicstaticvoidmain(String[]args)throwsInterruptedException{init();for(inti=0;i<10;i++){StringorderId="OID000000"+i;jedis.getResource().setex(orderId,3,orderId);System.out.println(System.currentTimeMillis()+"ms:"+orderId+"订单生成");}}staticclassRedisSubextendsJedisPubSub{@OverridepublicvoidonMessage(Stringchannel,Stringmessage){System.out.println(System.currentTimeMillis()+"ms:"+message+"订单取消");}}}