Java代码生成计时器
1. 背景
由于项目前端界面中需要计时器,而前端又存储不住数据,即使存储在cookis或者session里,在清空cookis后存在较大的安全隐患,所以需要后端也使用Java代码去生成计时器,同时能够实现多个计时器同时计时
2. 计时器类
点击查看代码
package com.example.pmxt.common;
//这里可以实现runable接口,也可以继承Thread类,runable与Thread的关系是实现,Thread是Runable的实现类,比Runable多了些方法,但两者并无本质区别
public class Countdown extends Thread{
private boolean stop = false;
private boolean pause = false;
private int time;
private int initialtime;
//这里可以是任何名字
private final Object lock = new Object();
@Override
public void run() {
while (!stop && time > 0) {
while (pause) {
onPause();
}
time--;
try {
Thread.sleep(1000);
int hh = time / 60 / 60 % 60;
int mm = time / 60 % 60;
int ss = time % 60;
System.out.println("还剩" + hh + "小时" + mm + "分钟" + ss + "秒");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 这个方法只能在run 方法中实现,不然会阻塞主线程,导致页面无响应,保证只会有一个线程挂起
*/
void onPause() {
synchronized (lock) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//开始时间
public int start(int time) {
this.time=time;
this.initialtime=time;
new Thread(this).start();
return time;
}
//彻底停止计时器,无法恢复
public void stopThread() {
this.stop = true;
}
//暂停时间
public int pauseThread() {
this.pause = true;
return time;
}
//恢复倒计时
public int resumeThread() {
this.pause = false;
synchronized (lock) {
//恢复挂起的线程
lock.notify();
}
return time;
}
//重置倒计时
public void reset(){
this.time = initialtime;
}
//获取当前时间
public int getTime(){
return time;
}
}
3. Seivice
点击查看代码
package com.example.pmxt.modules.jmzt;
import com.example.pmxt.common.BaseService;
import com.example.pmxt.common.Countdown;
import com.example.pmxt.domain.Jmzt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.sql.Time;
import java.util.*;
@Service
@Transactional
public class JmztService extends BaseService<JmztMapper, Jmzt> {
private final JmztMapper jmztMapper;
//计时器键值对,实现多个计时器的存储
Map<String,Object> countdowns= new HashMap<>();
//帮助前端存储变量
Map<String,Boolean> selects = new HashMap<>();
// int num =1;
@Autowired
public JmztService(JmztMapper jmztMapper) {
this.jmztMapper = jmztMapper;
}
//回退
public Boolean back(String bdbh) {
int[] a = jmztMapper.idcol(bdbh);
System.out.println(Arrays.toString(a));
int b = 0;
int c = 0;
int d = 0;
if (a == null||a.length == 0){
return false;
}else if (a.length > 1) {
b = jmztMapper.setBack(a[0]);
c = jmztMapper.setUp(a[1]);
} else {
d = jmztMapper.setBack(a[0]);
}
return b != 0 || c != 0 || d != 0;
}
//出价
public boolean bid(String bdbh, String jmh, BigDecimal cj,String pmhbh,Boolean select){
Countdown countdown = (Countdown) countdowns.get(pmhbh);
int[] a = jmztMapper.idcol(bdbh);
jmztMapper.bid(bdbh, jmh, cj);
if(select){
countdown.reset();
}
int[] b = jmztMapper.idcol(bdbh);
System.out.println(Arrays.toString(a));
System.out.println(Arrays.toString(b));
int c;
if (b == null||b.length - a.length == 0){
return false;
}else if (b.length - a.length == 1){
if (b.length ==1) {
return true;
} else {
c = jmztMapper.setOut(b[1]);
return c!=0;
}
}else return false;
}
//修改加价幅度
public int updJjfd(String bdbh, BigDecimal jjfd){
return jmztMapper.updJjfd(bdbh, jjfd);
}
//开拍
public int bdBegin(String bdbh){
return jmztMapper.bdBegin(bdbh);
}
//竞价记录
public List<Jmzt> record(String bdbh){
return jmztMapper.record(bdbh);
}
//标的成交
public int bdSuccess(String bdbh){
return jmztMapper.bdSuccess(bdbh);
}
//标的流拍
public int bdFailure(String bdbh){
return jmztMapper.bdFailure(bdbh);
}
// 开始倒计时
public int startTime(String pmhbh, Time temtime,Boolean select){
if(countdowns.get(pmhbh) == null){
Countdown countdown = new Countdown();
selects.put(pmhbh,select);
countdowns.put(pmhbh,countdown);
int time1;
time1 = countdown.start(temtime.getHours()*3600+temtime.getMinutes()*60+temtime.getSeconds());
return time1;
}else {
Countdown countdown = (Countdown) countdowns.get(pmhbh);
countdown.stopThread();
countdowns.remove(pmhbh, countdown);
return 0;
}
// num++;
// int time;
// if (select) {
// time = jmztMapper.getByBdbh(bdbh).getYsjjsj().getHours()*3600+jmztMapper.getByBdbh(bdbh).getYsjjsj().getMinutes()*60+jmztMapper.getByBdbh(bdbh).getYsjjsj().getSeconds();
// time1 = countdown.start(time);
// }else {
// time = jmztMapper.getByBdbh(bdbh).getXsjjsj().getHours()*3600+jmztMapper.getByBdbh(bdbh).getXsjjsj().getMinutes()*60+jmztMapper.getByBdbh(bdbh).getXsjjsj().getSeconds();
// time1 = countdown.start(time);
// }
}
// 停止倒计时
public int stopTime(String pmhbh){
Countdown countdown = (Countdown) countdowns.get(pmhbh);
if(countdown == null){
return 0;
}else {
return countdown.pauseThread();
}
}
// 继续倒计时
public int continueTime(String pmhbh){
Countdown countdown = (Countdown) countdowns.get(pmhbh);
if(countdown == null){
return 0;
}else {
return countdown.resumeThread();
}
}
// 重置倒计时
public Boolean resetTime(String pmhbh){
Countdown countdown = (Countdown) countdowns.get(pmhbh);
if(countdown == null){
return false;
}else {
countdown.reset();
return true;
}
}
// 注销计时器
public Boolean delTimer(String pmhbh){
if(countdowns.get(pmhbh) == null){
return false;
}else {
Countdown countdown = (Countdown) countdowns.get(pmhbh);
countdown.stopThread();
countdowns.remove(pmhbh, countdown);
return true;
}
}
// 获取当前时间
public Map<String,Object> getTime(String pmhbh){
Map<String,Object> map = new HashMap<>();
if(countdowns.get(pmhbh) == null){
map.put("time",0);
}else {
Countdown countdown = (Countdown) countdowns.get(pmhbh);
map.put("time",countdown.getTime());
}
if(selects.get(pmhbh) == null){
map.put("select","不存在");
}else {
map.put("select",selects.get(pmhbh));
}
return map;
}
}
4. Controller类
点击查看代码
package com.example.pmxt.modules.jmzt;
import com.example.pmxt.common.ReturnResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
import java.sql.Time;
import java.util.Map;
@RestController
@Validated
@RequestMapping("/jmzts")
public class JmztController {
private final JmztService jmztService;
@Autowired
public JmztController(JmztService jmztService) {
this.jmztService = jmztService;
}
//回退
@PostMapping("/back")
public ReturnResult back(String bdbh){
return ReturnResult.buildSuccessResult(jmztService.back(bdbh));
}
//出价
@PostMapping("/bid")
public ReturnResult bid(String bdbh, String jmh, BigDecimal cj, String pmhbh,Boolean select){
return ReturnResult.buildSuccessResult(jmztService.bid(bdbh, jmh, cj,pmhbh,select));
}
//修改加价幅度
@PostMapping("/updJjfd")
public ReturnResult updJjfd(String bdbh, BigDecimal jjfd){
return ReturnResult.buildSuccessResult(jmztService.updJjfd(bdbh, jjfd));
}
//标的开拍
@PutMapping("/bdBegin")
public ReturnResult bdBegin(String bdbh){
return ReturnResult.buildSuccessResult(jmztService.bdBegin(bdbh));
}
//竞价记录
@GetMapping("/record")
public ReturnResult record(String bdbh){
return ReturnResult.buildSuccessResult(jmztService.record(bdbh));
}
//标的成交
@PutMapping("/bdSuccess")
public ReturnResult bdSuccess(String bdbh){
return ReturnResult.buildSuccessResult(jmztService.bdSuccess(bdbh));
}
//标的流拍
@PutMapping("/bdFailure")
public ReturnResult bdFailure(String bdbh){
return ReturnResult.buildSuccessResult(jmztService.bdFailure(bdbh));
}
//开始计时
@PostMapping("/startTime")
public ReturnResult startTime(String pmhbh, Time temtime,Boolean select){
int time = jmztService.startTime(pmhbh,temtime,select);
if (time == 0){
return ReturnResult.buildSuccessResult("计时器已经存在,已经帮您注销,请再次点击");
}else {
return ReturnResult.buildSuccessResult(time);
}
}
//停止倒计时
@PostMapping("/stopTime")
public ReturnResult stopTime(String pmhbh){
int time = jmztService.stopTime(pmhbh);
if (time == 0){
return ReturnResult.buildSuccessResult("计时器不存在");
}else {
return ReturnResult.buildSuccessResult(time);
}
}
//继续倒计时
@PostMapping("/continueTime")
public ReturnResult continueTime(String pmhbh){
int time = jmztService.continueTime(pmhbh);
if (time == 0){
return ReturnResult.buildSuccessResult("计时器不存在");
}else {
return ReturnResult.buildSuccessResult(time);
}
}
//重置倒计时
@PostMapping("/resetTime")
public ReturnResult resetTime(String pmhbh){
Boolean time = jmztService.resetTime(pmhbh);
if (time){
return ReturnResult.buildSuccessResult("重置成功");
}else {
return ReturnResult.buildSuccessResult("计时器不存在");
}
}
//注销计时器
@PostMapping("/deleteTimer")
public ReturnResult delTimer(String pmhbh){
Boolean time = jmztService.delTimer(pmhbh);
if (time){
return ReturnResult.buildSuccessResult("删除成功");
}else {
return ReturnResult.buildSuccessResult("计时器不存在");
}
}
//获取当前时间
@PostMapping("/getTime")
public ReturnResult getTime(String pmhbh){
Map<String, Object> map = jmztService.getTime(pmhbh);
if ((int)map.get("time") == 0){
return ReturnResult.buildSuccessResult("计时器不存在");
}else {
return ReturnResult.buildSuccessResult(map);
}
}
}
5. 出现的错误
注意上述计时器用的是继承Thread类,如果实现runable会出现在idea中没问题,二到服务器上或者用tomcat会出现暂停后无法继续的问题,错误的计时器如下:
点击查看代码
package com.example.pmxt.common;
public class countdown implements Runnable{
private boolean stop;
private boolean pause;
private int time;
private int initialtime;
@Override
public void run() {
while (!stop) {
//不加输出语句服务器会无法继续
System.out.println("------------------");
if (!pause) {
time--;
try {
Thread.sleep(1000);
int hh = time / 60 / 60 % 60;
int mm = time / 60 % 60;
int ss = time % 60;
System.out.println("还剩" + hh + "小时" + mm + "分钟" + ss + "秒");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public int start(int time) {
this.time=time;
this.initialtime=time;
new Thread(this).start();
return time;
}
public void stop() {
this.stop = true;
}
public int pause() {
this.pause = true;
return time;
}
public int resume() {
this.pause = false;
return time;
}
public void reset(){
this.time = initialtime;
}
public int getTime(){
return time;
}
// public static long getCount() {
//
// }
}
导致原因:stop,pause没在内存中,无法改变其值,这两个在工作内存中。
解决办法有两个:1.加输出语句 2.在stop、pause属性上加volatile
详细如下:
Java基础:volatile详解
6. 后续
发现hutool也是有计时器的,不过没有用过,有需要的可以去看一下
hutool计时器
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?