本文使用jdk自带工具实现
依赖jar包
# 怕不兼容jdk1.6使用老版本jar
commons-beanutils-1.6.jar
commons-collections-3.2.1.jar
commons-lang-2.1.jar
commons-logging-1.1.1.jar
ezmorph-1.0.1.jar
ezmorph-1.0.2.jar
json-lib-2.4-jdk15.jar
mysql-connector-java-5.0.5-bin.jar
HttpURLConnection
// 使用JDK自带Timer 和 HttpURLConnection 多任务使用ScheduledExecutorService
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
public void run() {
List userList = new ArrayList();
List orgList = new ArrayList();
try {
String URL="jdbc:mysql://localhost/data?characterEncoding=utf-8";
String USER="data";
String PASSWORD="data";
// 1.加载驱动程序
Class.forName("com.mysql.jdbc.Driver");
// 2.获得数据库连接
Connection conn=DriverManager.getConnection(URL, USER, PASSWORD);
// 3.通过数据库的连接操作数据库 实现增删改查(使用Statement类)
Statement st=conn.createStatement();
// ---用户信息开始
ResultSet userRs=st.executeQuery(
"SELECT" +
" EMP_ID" + // 用户id
" ,ACCOUNT" + // 用户账户
" ,ORG_ID" + // 用户部门id
" ,EMPNAME" + // 用户名
" ,EMP_PASSWORD" + // 密码
" ,ISDELETE" + // 用户是否删除 0未删除 1已删除
" FROM" +
" ORG_EMPLOYEE");
// 4.处理数据库的返回结果(使用ResultSet类)
userList = new ArrayList();
while (userRs.next()) {
Map user = new HashMap();
user.put("id", userRs.getString("EMP_ID"));
user.put("loginName", userRs.getString("ACCOUNT"));
user.put("userName", userRs.getString("EMPNAME"));
user.put("password", userRs.getString("EMP_PASSWORD"));
user.put("userCateId", userRs.getString("ORG_ID"));
userList.add(user);
}
userRs.close(); // 关闭资源
// ---用户信息结束
// ---部门信息开始
ResultSet orgRs = st.executeQuery(
"SELECT" +
" ORG_ID" +
" ,ORGNAME" +
" ,ORGPARENTORGID" +
" FROM" +
" ORG_ORGANIZATION");
// 4.处理数据库的返回结果(使用ResultSet类)
while (orgRs.next()) {
Map org = new HashMap();
org.put("id", orgRs.getString("ORG_ID"));
org.put("cateName", orgRs.getString("ORGNAME"));
org.put("parentId", orgRs.getString("ORGPARENTORGID"));
orgList.add(org);
}
orgRs.close(); // 关闭资源
// ---部门信息结束
st.close(); // 关闭资源
conn.close(); // 关闭资源
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
// 可直接在url上输入参数 中文要编码成UTF-8的字节数组后转成String
// String urlPath = new String("http://localhost:8080/data_sync?data=数据".getBytes("UTF-8"));
String urlPath = "http://localhost:8080/data_sync";
// 调用端如不自动解码 则可通过如下方式编码
// URLEncoder.encode(userList.toString(), "UTF-8")
// 调用端使用如下解码
// URLEncoder.decode(userList.toString(), "UTF-8")
String param = "userList=" + JSONArray.fromObject(userList).toString()
+ "&orgList=" + JSONArray.fromObject(orgList).toString()
+ "&token=t$o$k$e$n";
try {
// 服务地址
URL url = new URL(urlPath);
// 设定连接的相关参数
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// 设置是否向HttpURLConnection输出 post请求参数要放在http正文内需设为true 默认false
connection.setDoOutput(true);
// 设置请求方法类型为 默认为GET 此处必须为大写
connection.setRequestMethod("POST");
// 设置连接超时
connection.setConnectTimeout(5000);
// 设置读取超时
connection.setReadTimeout(5000);
// 开始连接 connection.getOutputStream()已包含此操作
// connection.connect();
// 如果响应内容乱码在此处添加编码名称 new OutputStreamWriter(connection.getOutputStream(), "UTF-8");
OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream());
// 发送参数
out.write(param);
out.flush();
out.close();
// 获取服务端的反馈
String strLine = "";
String strResponse = "";
InputStream in = connection.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
while((strLine = reader.readLine()) != null) {
strResponse += strLine + "\n";
}
System.out.println(strResponse);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("已经执行" + new SimpleDateFormat ("HH:mm:ss").format (new Date()));
}
}, 1000, 1000 * 60); // 此时为一分钟执行一次 每次延迟一秒执行 为下面最后一种写法
// schedule(TimerTask task, Date time) 在指定的时间执行某任务
// schedule(TimerTask task, Date firstTime, long period) 在指定的时间执行某任务并进行重复的某时间延迟执行
// schedule(TimerTask task, long delay) 在指定的某时间延迟执行某任务
// schedule(TimerTask task, long delay, long period) 在指定的某时间延迟执行某任务并进行重复的某时间延迟执行
}
调用方为Spring的Controller
@Controller
public class DataSyncController {
@Autowired
private SysUserService userService;
@Autowired
private SysUserCateService userCateService;
@ResponseBody
@PostMapping("data_sync")
public boolean test(String token, String userList, String orgList) {
if (!token.equals("t$o$k$e$n")) {
return false;
}
try {
JSONArray userJson = JSONArray.fromObject(userList); // 获取data字段
JSONArray orgJson = JSONArray.fromObject(orgList); // 获取data字段
List<SysUser> users = JSONArray.toList(userJson, new SysUser(), new JsonConfig());
List<SysUserCate> userCates = JSONArray.toList(orgJson, new SysUserCate(), new JsonConfig());
userService.syncData(users);
userCateService.sysnData(userCates);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
}
窗口运行
java -jar DataSync.jar
后台运行
# 运行java命令时 会出现并保持一个console窗口 程序中的信息可以通过System.out在console内输出
# 而运行javaw 开始时会出现console 当主程序调用之后 console就会消失 javaw大多用来运行GUI程序
# @echo off执行后 后面所有的命令均不显示 包括本条命令
# echo off执行后 后面所有的命令均不显示 但显示本条命令
# start 在新的窗口打开
@echo off
start "data-sync" javaw -jar DataSync.jar
exit # exit退出窗口命令可以省略
# 0 标准输入 一般是键盘
# 1 标准输出 一般是显示屏 是用户终端控制台
# 2 标准错误 错误信息输出
# 将运行的jar 错误日志信息输出到log.file文件中 然后 >&1就是继续输出到标准输出
#前面加的& 是为了让系统识别是标准输出 最后一个& 表示在后台运行
nohup java -jar DataSync.jar > log.file 2 > &1&
# 不指定日志配置可以简写如下 默认输出被重定向到nohup.out文件
nohup java -jar DataSync.jar&
Timer的多任务执行缺陷
public class TimerTest {
private static long start;
public static void main(String[] args) throws Exception {
TimerTask task1 = new TimerTask() {
@Override
public void run() {
System.out.println("task1 invoked ! "
+ (System.currentTimeMillis() - start));
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
TimerTask task2 = new TimerTask() {
@Override
public void run() {
System.out.println("task2 invoked ! "
+ (System.currentTimeMillis() - start));
}
};
Timer timer = new Timer();
start = System.currentTimeMillis();
timer.schedule(task1, 1000);
timer.schedule(task2, 3000);
}
}
定义了两个任务 预计是第一个任务1s后执行 第二个任务3s后执行 但是看运行结果
task1 invoked ! 1000
task2 invoked ! 4000
task2实际4后执行 Timer内部是一个线程 而任务1所需的时间超过了两个任务间的间隔导致
使用ScheduledThreadPool定时运行多任务
public class ScheduledThreadPoolExecutorTest {
private static long start;
public static void main(String[] args) {
// 使用工厂方法初始化一个ScheduledThreadPool
ScheduledExecutorService newScheduledThreadPool = Executors
.newScheduledThreadPool(2);
TimerTask task1 = new TimerTask() {
@Override
public void run() {
try {
System.out.println("task1 invoked ! "
+ (System.currentTimeMillis() - start));
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
}
}
};
TimerTask task2 = new TimerTask() {
@Override
public void run() {
System.out.println("task2 invoked ! "
+ (System.currentTimeMillis() - start));
}
};
start = System.currentTimeMillis();
newScheduledThreadPool.schedule(task1, 1000, TimeUnit.MILLISECONDS);
newScheduledThreadPool.schedule(task2, 3000, TimeUnit.MILLISECONDS);
}
}
输出结果
task1 invoked ! 1001
task2 invoked ! 3001
ScheduledThreadPool内部是个线程池 可以支持多个任务并发执行
Timer的抛出异常时执行缺陷
public class ScheduledThreadPoolTest {
public static void main(String[] args) throws InterruptedException {
final TimerTask task1 = new TimerTask() {
@Override
public void run() {
throw new RuntimeException();
}
};
final TimerTask task2 = new TimerTask() {
@Override
public void run() {
System.out.println("task2 invoked!");
}
};
Timer timer = new Timer();
timer.schedule(task1, 100);
timer.scheduleAtFixedRate(task2, new Date(), 1000);
}
}
上面两个任务 任务1抛出一个运行时的异常 任务2周期性的执行某个操作 输出结果
task2 invoked!
Exception in thread "Timer-0" java.lang.RuntimeException
at com.java.ScheduledThreadPoolTest$1.run(ScheduledThreadPoolDemo01.java:24)
at java.util.TimerThread.mainLoop(Timer.java:512)
at java.util.TimerThread.run(Timer.java:462)
任务1抛出异常导致任务2也停止运行
使用ScheduledExecutorService抛异常运行多任务
public class ScheduledThreadPoolTest {
public static void main(String[] args) throws InterruptedException {
final TimerTask task1 = new TimerTask() {
@Override
public void run() {
throw new RuntimeException();
}
};
final TimerTask task2 = new TimerTask() {
@Override
public void run() {
System.out.println("task2 invoked!");
}
};
ScheduledExecutorService pool = Executors.newScheduledThreadPool(1);
pool.schedule(task1, 100, TimeUnit.MILLISECONDS);
pool.scheduleAtFixedRate(task2, 0, 1000, TimeUnit.MILLISECONDS);
}
}
代码基本一致 但是ScheduledExecutorService可以保证 task1出现异常时 不影响task2的运行
task2 invoked!
task2 invoked!
task2 invoked!
Timer执行周期任务时依赖系统时间 如果当前系统时间发生变化会出现一些执行上的变化
ScheduledExecutorService基于时间的延迟 即相对时间 不会由于系统时间的改变发生执行变化
SpringBoot自带定时任务
@EnableScheduling // 开启对定时任务支持
@SpringBootApplication
public class DmsAdminApplication{
public static void main(String[] args) {
SpringApplication.run(DmsAdminApplication.class, args);
}
}
@Component
// 启动类写了如下注解 此处可省略书写
@EnableScheduling
public class TestTask {
// 配置毫秒数
@Scheduled(fixedRate = 1000) // 毫秒
public void reportCurrentTime() {
System.out.println ("已经执行" + new SimpleDateFormat ("HH:mm:ss").format (new Date()));
}
// cron表达式写法
// @Scheduled(cron = "* * * * *") // 每1分钟执行一次
// @Scheduled(cron = "*/1 * * * *") // 每1分钟执行一次
@Scheduled(cron = "0 */2 * * *") // 每两个小时执行一次
public void reportCurrentByCron() {
System.out.println ("已经执行" + new SimpleDateFormat ("HH:mm:ss").format (new Date ()));
}
}
cron表达式有7个域 依序分别为 秒 分 时 日 月 周 年 年为可选类型 在不设定年分时为每年
cron字符描述
字符 | 描述 |
---|---|
* |
匹配所有的值 如 *在分钟的字段域里表示 每分钟 |
? |
只在日期域和星期域中使用 它被用来指定非明确的值 |
- |
指定范围 如 10-12在小时域意味着10点、11点、12点 |
, |
指定多个值 如 MON WED FRI在星期域里表示星期一 星期三 星期五 |
/ |
指定增量 如 */1 * * * * 每1分钟执行一次 |
L |
表示day-of-month和day-of-week域 但在两个字段中的意思不同 例如day-of-month域中表示一个月的最后一天 如果在day-of-week域表示’7’或者’SAT’ 如果在day-of-week域中前面加上数字 它表示一个月的最后几天 例如’6L’就表示一个月的最后一个星期五 |
W |
只允许日期域出现 这个字符用于指定日期的最近工作日 例如 在日期域中写15W表示 此月15号最近的工作日 所以 如果15号是周六 则任务会在14号触发 如果15好是周日 则任务会在周一也就是16号触发 如果是在日期域填写1W即使1号是周六 那么任务也只会在下周一 也就是3号触发 W字符指定的最近工作日是不能够跨月份的 字符W只能配合一个单独的数值使用 不能够是一个数字段 如 1-15W是错误的 |
LW |
L和W可以在日期域中联合使用 LW表示这个月最后一周的工作日 |
# |
只允许在星期域中出现 指定本月的某天 如 6#3表示本月第三周的星期五 6表示星期五 3表示第三周 |
C |
允许在日期域和星期域出现 此表达式值依赖于相关日历计算 无日历关联 则等价于所有包含的日历 如 日期域是5C表示关联日历中第一天 或者这个月开始的第一天的后5天 星期域是1C表示关联日历中第一天 或者星期的第一天的后1天 也就是周日的后一天周一 |
cron字符对应意义
字段 | 允许值 | 允许的特殊字符 |
---|---|---|
秒 | 0-59 | , - * / |
分 | 0-59 | , - * / |
小时 | 0-23 | , - * / |
月内日期 | 1-31 | , - * ? / L W C |
月 | 1-12 或者 JAN-DEC | , - * / |
周内日期 | 1-7 或者 SUN-SAT | , - * ? / L C # |
年(可选) | 留空 1970-2099 | , - * / |
cron常用写法
表达式 | 作用 |
---|---|
* * * * * |
每1分钟执行一次 |
*/1 * * * * |
每1分钟执行一次 |
0 0 1 * * ? |
每天1点触发 |
0 10 1 ? * * |
每天早上1点10分触发 |
0 10 1 * * ? |
每天早上1点10分触发 |
0 10 1 * * ? * |
每天早上1点10分触发 |
0 10 1 * * ? 2020 |
2020年的每天1点10分触发 |
0 * 2 * * ? |
每天1点到1点59分每分钟一次触发 |
0 0/5 14 * * ? |
每天从下午2点开始到2:55分结束每5分钟一次触发 |
0 0/5 1,3 * * ? |
每天1点至1点55分 6点至6点55分每5分钟一次触发 |
0 5-10 1 * * ? |
每天1点5分至1点10分每分钟一次触发 |
0 10,15 1 ? 3 WED |
三月的每周三的1点10分和1点15分触发 |
0 10 1 ? * MON-FRI |
每周 周一至周五1点10分触发 |
持续更新。。。 |