Java基础系列6:计时器Timer与新闻的定时自动采集

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
简介:

一 Timer类与TimerTask类

      在Java中要实现定时执行某项任务就需要用到Timer类和TimerTask类。其中,Timer类可以实现在某一刻时间或某一段时间后安排某一个任务执行一次或定期重复执行,该功能需要与TimerTask类配合使用。TimerTask类表示由Timer类安排的一次或多次重复执行的那个任务。

      Timer类中的常用方法:

方法 描述
void cancel() 终止此计时器,丢弃所有当前已安排的任务,对当前正在执行的任务没有影响
int purge() 从此计时器的任务队列中移除所有已取消的任务,一般用来释放内存空间
void schedule(TimerTask task, Date time) 安排在指定的时间执行指定的任务
void schedule(TimerTask task, Date firstTime, long period) 安排指定的任务在指定的时间开始进行重复的固定延迟执行
void schedule(TimerTask task, long delay)  安排在指定延迟后执行指定的任务
void schedule(TimerTask task, long delay, long period) 安排指定的任务从指定的延迟后开始进行重复的固定延迟执行
void scheduleAtFixedRate(TimerTask task, Date firstTime, long period) 安排指定的任务在指定的时间开始进行重复的固定速率执行
void scheduleAtFixedRate(TimerTask task, long delay, long period)  安排指定的任务在指定的延迟后开始进行重复的固定速率执行

注:1)上面提到的时间的单位都是毫秒

      2)schedule()方法和scheduleAtFixedRate()方法的区别在于重复执行任务时对于时间间隔出现延迟的情况的处理:schedule()方法的执行时间间隔永远是固定的,如果之前出现了延迟情况,那么之后也会继续按照设定好的时间间隔来执行;scheduleAtFixedRate()方法在出现延迟情况时,则将快速连续地出现两次或更多的执行,从而使后续执行能够“追赶上来”。从长远来看,执行的频率将正好是指定周期的倒数(假定 Object.wait(long) 所依靠的系统时钟是准确的)

二 一个定时任务范例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package  javase.timer;
 
import  java.text.Format;
import  java.text.SimpleDateFormat;
import  java.util.Date;
import  java.util.Timer;
import  java.util.TimerTask;
 
class  MyTask  extends  TimerTask{
     public  void  run() {
         Format format =  new  SimpleDateFormat( "yyyy-MM-dd HH:mm:ss:SSS" );
         System.out.println( "当前时间:"  + format.format( new  Date()));    
     }
     
}
 
public  class  Demo {
 
     public  static  void  main(String[] args) {
         Timer timer =  new  Timer();
         timer.schedule( new  MyTask(),  1000 100 );   //1秒后执行,并且每隔100毫秒重复执行
         
         try  {
             Thread.sleep( 2000 );
         catch  (InterruptedException e) {
             e.printStackTrace();
         }
         
         timer.cancel();   //终止计时器,放弃所有已安排的任务
         timer.purge();   //释放内存
     }
 
}

输出:

1
2
3
4
5
6
7
8
9
当前时间: 2016 - 03 - 01  15 : 02 : 36 : 451
当前时间: 2016 - 03 - 01  15 : 02 : 36 : 477
当前时间: 2016 - 03 - 01  15 : 02 : 36 : 578
当前时间: 2016 - 03 - 01  15 : 02 : 36 : 678
当前时间: 2016 - 03 - 01  15 : 02 : 36 : 778
当前时间: 2016 - 03 - 01  15 : 02 : 36 : 878
当前时间: 2016 - 03 - 01  15 : 02 : 36 : 978
当前时间: 2016 - 03 - 01  15 : 02 : 37 : 078
当前时间: 2016 - 03 - 01  15 : 02 : 37 : 178

      从上面的代码可以看出,操作步骤跟线程是差不多的,首先是用一个类实现TimerTask接口,然后在run()方法里定义具体的任务,最后通过一个Timer实例定时调用这个任务执行(PS:实际上TimerTask实现了runnable接口,因此就少了我们很多的操作)

三 使用Timer实现的新闻定时自动采集

      在这里,我选用的测试目标是“中国新闻网”的滚动新闻,链接是:http://www.chinanews.com/scroll-news/news1.html 。然后接下来的步骤就很明确了,设置定时任务定时去访问这个网页,然后每次访问时用正则表达式将我们需要的最新新闻的标题、URL、类型和时间提取出来,最后是保存到数据库中。完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package  javase.timer;
 
import  java.io.BufferedReader;
import  java.io.IOException;
import  java.io.InputStream;
import  java.io.InputStreamReader;
import  java.net.HttpURLConnection;
import  java.net.MalformedURLException;
import  java.net.URL;
import  java.sql.Connection;
import  java.sql.PreparedStatement;
import  java.sql.ResultSet;
import  java.sql.SQLException;
import  java.util.ArrayList;
import  java.util.List;
import  java.util.Timer;
import  java.util.TimerTask;
import  java.util.regex.Matcher;
import  java.util.regex.Pattern;
 
class  CollectionTask  extends  TimerTask {
 
     public  void  run() {
         synchronized  ( this ) {
             List<String[]> news = getCurrentNews();
             if  (news.size() >  0 )
                 saveNews(news);
         }
 
     }
 
     /**
      * 获取最新的“中国新闻网”的滚动新闻
     
      * @return 最新滚动新闻的list列表
      * */
     private  List<String[]> getCurrentNews() {
         List<String[]> newsList =  new  ArrayList<String[]>();
 
         try  {
             URL url =  new  URL( "http://www.chinanews.com/scroll-news/news1.html" );
             HttpURLConnection connection = (HttpURLConnection) url
                     .openConnection();
             connection.setRequestMethod( "GET" );
             connection.setConnectTimeout( 5000 );
             connection.setReadTimeout( 5000 );
 
             if  (connection.getResponseCode() ==  200 ) {
                 InputStream inputStream = url.openStream();
                 // 注意编码问题,因为目标网页用的是gb2312,因此我这里也设置成gb2312,不然容易导致乱码
                 BufferedReader reader =  new  BufferedReader(
                         new  InputStreamReader(inputStream,  "gb2312" ));
                 String line =  "" ;
                 Pattern pattern = Pattern
                         .compile( "<li><div class=\"dd_lm\">\\[<a href=.*?>(.+?)</a>\\]</div>.*?<div class=\"dd_bt\"><a href=\"([^\"]+)\">(.+)?</a></div><div class=\"dd_time\">(.+)?</div></li>" );
                 Matcher matcher =  null ;
                 while  ((line = reader.readLine()) !=  null ) {
                     // System.out.println(line);
                     matcher = pattern.matcher(line);
                     if  (matcher.find()) {
                         String[] news =  new  String[ 4 ];  // 存储每条新闻的各个元素的数组
                         news[ 0 ] = matcher.group( 1 );  // 类型
                         news[ 1 ] = matcher.group( 2 );  // URL
                         news[ 2 ] = matcher.group( 3 );  // 标题
                         news[ 3 ] = matcher.group( 4 );  // 时间
 
                         newsList.add(news);
                     }
                 }
                 // for(int i=0;i<newsList.size();i++){
                 // String[] temp = newsList.get(i);
                 // System.out.println("分类:" + temp[0] + "  标题:" + temp[2] +
                 // "  URL:" + temp[1] + "  时间:" + temp[3]);
                 // }
                 reader.close();
                 inputStream.close();
                 connection.disconnect();
                 return  newsList;
             }
 
         catch  (MalformedURLException e) {
             e.printStackTrace();
         catch  (IOException e) {
             e.printStackTrace();
         }
 
         return  newsList;
     }
 
     /**
      * 保存新闻列表到数据库
     
      * @param news
      *            待保存的新闻列表
      * */
     private  void  saveNews(List<String[]> news) {
         Connection connection = JDBCDemo.getConnection();  // 获取数据库连接
         try  {
             PreparedStatement pStatement =  null ;
 
             // 遍历,然后判断该条新闻是否已经在数据库中存在,不存在则保存
             for  (String[] temp : news) {
                 // System.out.println("分类:" + temp[0] + "  标题:" + temp[2] +
                 // "  URL:"
                 // + temp[1] + "  时间:" + temp[3]);
 
                 pStatement = connection
                         .prepareStatement( "select id from rollnews where url = ?" );
                 pStatement.setString( 1 , temp[ 1 ]);
                 ResultSet resultSet = pStatement.executeQuery();
                 if  (resultSet.next())
                     continue ;
                 else  {
                     pStatement = connection
                             .prepareStatement( "insert into rollnews(title,url,type,time) values(?,?,?,?)" );
                     pStatement.setString( 1 , temp[ 2 ]);
                     pStatement.setString( 2 , temp[ 1 ]);
                     pStatement.setString( 3 , temp[ 0 ]);
                     pStatement.setString( 4 , temp[ 3 ]);
 
                     pStatement.executeUpdate();
                 }
 
             }
             pStatement.close();
             connection.close();
         catch  (SQLException e) {
             e.printStackTrace();
         }
 
     }
 
}
 
public  class  NewsCollection {
 
     public  static  void  main(String[] args) {
         Timer timer =  new  Timer();
         // timer.schedule(new CollectionTask(), 1000, 120000);
         timer.scheduleAtFixedRate( new  CollectionTask(),  1000 120000 );
     }
 
}

附:

(1JDBCDemo.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package  javase.timer;
 
import  java.sql.Connection;
import  java.sql.DriverManager;
import  java.sql.SQLException;
 
public  class  JDBCDemo {
 
     public  static  Connection getConnection(){
         try  {
             Class.forName( "com.mysql.jdbc.Driver" );
             
             return  DriverManager.getConnection( "jdbc:mysql://127.0.0.1:3306/news?useUnicode=true&characterEncoding=utf-8" "root" "root" );
         catch  (ClassNotFoundException e) {
             e.printStackTrace();
         catch  (SQLException e) {
             e.printStackTrace();
         }
         return  null ;
     }
 
}

(2)SQL结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/*
Navicat MySQL Data Transfer
 
Source Server         : sel
Source Server Version : 50519
Source Host           : localhost:3306
Source  Database        : news
 
Target Server Type    : MYSQL
Target Server Version : 50519
File Encoding         : 65001
 
Date : 2016-03-01 15:35:06
*/
 
SET  FOREIGN_KEY_CHECKS=0;
 
-- ----------------------------
-- Table structure for rollnews
-- ----------------------------
DROP  TABLE  IF EXISTS `rollnews`;
CREATE  TABLE  `rollnews` (
   `id`  int (11)  NOT  NULL  AUTO_INCREMENT,
   `title`  varchar (255)  NOT  NULL ,
   `url`  varchar (255)  NOT  NULL ,
   `type`  varchar (255)  NOT  NULL ,
   ` time varchar (255)  NOT  NULL ,
   PRIMARY  KEY  (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=300  DEFAULT  CHARSET=utf8;

最后的效果如下:

wKioL1bZqzGAFqPZAAEoi8ksRuo568.png

每隔2分钟会访问目标网页一次,然后将获取到的新的新闻保存到数据库中




本文转自 pangfc 51CTO博客,原文链接:http://blog.51cto.com/983836259/1747746,如需转载请自行联系原作者
相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
2月前
|
Java 调度
Java实现定时启动,且只执行一次,如何实现?
【10月更文挑战第18天】Java实现定时启动,且只执行一次,如何实现?
262 3
|
6月前
|
安全 Java 调度
使用 Java Timer 实现任务调度
使用 Java Timer 实现任务调度
|
2月前
|
存储 缓存 Java
java基础:IO流 理论与代码示例(详解、idea设置统一utf-8编码问题)
这篇文章详细介绍了Java中的IO流,包括字符与字节的概念、编码格式、File类的使用、IO流的分类和原理,以及通过代码示例展示了各种流的应用,如节点流、处理流、缓存流、转换流、对象流和随机访问文件流。同时,还探讨了IDEA中设置项目编码格式的方法,以及如何处理序列化和反序列化问题。
87 1
java基础:IO流 理论与代码示例(详解、idea设置统一utf-8编码问题)
|
6月前
|
Java
【Java基础】输入输出流(IO流)
Java基础、输入输出流、IO流、流的概念、输入输出流的类层次结构图、使用 InputStream 和 OutputStream流类、使用 Reader 和 Writer 流类
186 2
|
2月前
|
监控 Java
Java定时扫码一个文件夹下的文件,如何保证文件写入完成后才进行处理?
【10月更文挑战第13天】Java定时扫码一个文件夹下的文件,如何保证文件写入完成后才进行处理?
137 1
|
3月前
|
安全 Java API
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
String常量池、String、StringBuffer、Stringbuilder有什么区别、List与Set的区别、ArrayList和LinkedList的区别、HashMap底层原理、ConcurrentHashMap、HashMap和Hashtable的区别、泛型擦除、ABA问题、IO多路复用、BIO、NIO、O、异常处理机制、反射
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
|
4月前
|
存储 运维 Java
函数计算产品使用问题之怎么配置定时触发器来调用Java函数
函数计算产品作为一种事件驱动的全托管计算服务,让用户能够专注于业务逻辑的编写,而无需关心底层服务器的管理与运维。你可以有效地利用函数计算产品来支撑各类应用场景,从简单的数据处理到复杂的业务逻辑,实现快速、高效、低成本的云上部署与运维。以下是一些关于使用函数计算产品的合集和要点,帮助你更好地理解和应用这一服务。
|
6月前
|
安全 Java
|
6月前
|
NoSQL Java Serverless
Serverless 应用引擎产品使用合集之Java如何使用ScheduledExecutorService来实现定时触发
阿里云Serverless 应用引擎(SAE)提供了完整的微服务应用生命周期管理能力,包括应用部署、服务治理、开发运维、资源管理等功能,并通过扩展功能支持多环境管理、API Gateway、事件驱动等高级应用场景,帮助企业快速构建、部署、运维和扩展微服务架构,实现Serverless化的应用部署与运维模式。以下是对SAE产品使用合集的概述,包括应用管理、服务治理、开发运维、资源管理等方面。
|
6月前
|
搜索推荐 算法 Java
【Java基础】 几种简单的算法排序
几种简单的JAVA算法排序
54 4