Java 定时器Timer与TimeTask

项目实战

定时鼠标自动单击桌面

  • Java中提供了很多用于自动化测试包,可以用来模拟用户进行如移动鼠标、单击鼠标、滚动鼠标滚轮、右击、按下一个或多个键盘按键等
  • 下面的代码就是模拟用户自动单击桌面指定区域,每间隔1分钟,鼠标自动点击一次,测试一个晚上,时间误差完全可以忽略,非常稳定

代码实现

package test;

import java.awt.*;
import java.awt.event.InputEvent;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Created by Administrator on 2018/6/23 0023.
 * 创建模拟用户单击的定时器任务
 * 可以创建一次执行或重复执行的任务
 * TimeTask类似Runnable接口
 */
public class ClickTask extends TimerTask {
    /**
     * 用户单击计数
     */
    private AtomicInteger atomicInteger = new AtomicInteger(0);

    /**
     * 同样实现run方法
     */
    @Override
    public void run() {
        /**
         * 单数次与偶数次分别单击桌面两个不同的位置
         */
        if (atomicInteger.addAndGet(1) % 2 == 0) {
            clickScreenByXY(100, 300);
        } else {
            clickScreenByXY(1200, 300);
        }
        System.out.println(new Date() + " click " + atomicInteger.get() + " time ,Thread name is :" + Thread.currentThread().getName());
    }

    /**
     * 模拟用户单击屏幕指定区域,默认单击屏幕最中央
     *
     * @param xx坐标
     * @param yy坐标
     */
    public static final void clickScreenByXY(Integer x, Integer y) {
        try {
            /**创建工具包对象*/
            Toolkit toolkit = Toolkit.getDefaultToolkit();
            /**创建自动化对象*/
            Robot robot = new Robot();
            /**利用工具包对象获取屏幕分辨率*/
            if (x == null) {
                x = toolkit.getScreenSize().width / 2;
            }
            if (y == null) {
                y = toolkit.getScreenSize().height / 2;
            }
            /**
             * 移动鼠标到指定位置
             * 然后按下鼠标左键,再松开,模拟单击操作
             */
            robot.mouseMove(x, y);
            robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
            robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
            robot.delay(100);
        } catch (AWTException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        /**
         * 创建任务对象
         */
        TimerTask timerTask = new ClickTask();
        /**
         * 创建定时器对象,相当于新开一个线程去执行定时器任务
         */
        Timer timer = new Timer();
        /**
         * 定时器执行定时器任务,10秒后开始执行任务
         * 任务执行完成后,间隔1分钟再次执行,循环往复
         */
        timer.schedule(timerTask, 10000, 60000);

    }
}

运行结果


  • 同时间隔1分钟单击一次

定时往服务端发送心跳

  • 做服务端客户端通信项目时,因为采用UDP通信,所以让客户端间隔1分钟就往服务端做http的get请求,表示通信正常,客户端在线

代码实现

import com.lct.thread.HeartbeatTimerTask;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

import java.util.Timer;
import java.util.TimerTask;

/**
 * 应用程序入口
 * 1、继承Application
 */
public class MainApp extends Application {
    /**
     * 实现start方法
     */
    @Override
    public void start(Stage primaryStage) throws Exception {
        /**创建一个堆栈面板,面板用于包含控件*/
        StackPane root = new StackPane();

        /**设置窗口(舞台)标题并为舞台添加场景*/
        primaryStage.setTitle("gxg_client");
        primaryStage.setScene(new Scene(root, 500, 200));
        /**设置窗口无法缩放大小,默认为true,用户可以调节窗口大小*/
        primaryStage.setResizable(false);
        primaryStage.setIconified(true);

        /**
         * 使用定时器执行心跳任务--即定时往服务端发送消息
         * 指定定时器线程名称,以及指定为守护线程,这样主线程关闭后,定时器线程就会结束,应用会退出|
         * 指定此定时器任务立即执行,且任务执行完之后,延迟1分钟再次执行,依次循环往复
         */
        TimerTask timerTask = new HeartbeatTimerTask();
        Timer timer = new Timer("heartbeat thread", true);
        timer.schedule(timerTask, 0, 1 * 5 * 1000);

        /**显示窗口*/
        primaryStage.show();
    }

    public static void main(String[] args) {
        /**
         * 调用Applicationlaunch方法启动应用
         */
        launch(args);
    }
}
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ResourceBundle;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicInteger;

import com.lct.utils.SystemUtils;
import org.apache.http.HttpEntity;
import org.apache.http.StatusLine;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.WinHttpClients;
import org.apache.http.util.EntityUtils;

/**
 * Created by Administrator on 2018/6/26 0026.
 * http://192.168.1.20:8080/gxg_server/clientController/heartbeat.action?clientIp=xx&clientMac=yyy
 */
public class HeartbeatTimerTask extends TimerTask {

    /**
     * serverHeartbeatRequestPath:服务器心跳请求路径
     * getCountAtomicInteger:记录客户端请求的次数
     */
    private static final StringBuilder serverHeartbeatRequestPath = new StringBuilder();
    private static final AtomicInteger getCountAtomicInteger = new AtomicInteger(0);

    static {
        try {
            ResourceBundle resourceBundle = ResourceBundle.getBundle("systemConfig");
            serverHeartbeatRequestPath.append(resourceBundle.getString("serverHeartbeatRequestPath"));
            InetAddress inetAddress = InetAddress.getLocalHost();
            String clientIp = inetAddress.getHostAddress();
            String clientMac = SystemUtils.getLocalMac();
            serverHeartbeatRequestPath.append("?clientIp=" + clientIp);
            serverHeartbeatRequestPath.append("&clientMac=" + clientMac);
            serverHeartbeatRequestPath.append("&clientPort=" + resourceBundle.getString("localUdpListenerPort"));
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        if (!serverHeartbeatRequestPath.toString().startsWith("http")) {
            System.out.println("资源文件配置的服务器请求地址错误,放弃请求...");
            return;
        }
        if (getCountAtomicInteger.addAndGet(1) != 1) {
            int getCountIndex = serverHeartbeatRequestPath.indexOf("getCount=");
            serverHeartbeatRequestPath.replace(getCountIndex + 9, serverHeartbeatRequestPath.length(), getCountAtomicInteger.get() + "");
        } else {
            serverHeartbeatRequestPath.append("&getCount=" + getCountAtomicInteger.get());
        }
        CloseableHttpClient httpclient = WinHttpClients.createDefault();

        RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(5000)
                .setConnectionRequestTimeout(5000)
                .setSocketTimeout(5000)
                .setRedirectsEnabled(true)
                .build();

        HttpGet httpget = null;
        CloseableHttpResponse response = null;

        try {
            httpget = new HttpGet(serverHeartbeatRequestPath.toString());
            httpget.setConfig(requestConfig);

            response = httpclient.execute(httpget);
            StatusLine statusLine = response.getStatusLine();
            int statusCode = statusLine.getStatusCode();
            System.out.println("statusCode:" + statusCode);
            if (statusCode == 200) {
                HttpEntity httpEntity = response.getEntity();
                String feedback = EntityUtils.toString(httpEntity, "UTF-8");
                System.out.println("feedback:" + feedback);
            }
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                EntityUtils.consume(response.getEntity());
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (httpget != null && !httpget.isAborted()) {
                httpget.releaseConnection();
                httpget.abort();
            }
            if (response != null) {
                try {
                    response.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (httpclient != null) {
                try {
                    httpclient.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

错误结果分析

  • TimerTask实现的也是Runnable接口
  • Runnable 的run() 或者 Callable的call() 方法执行完的时候线程就会自动结束
  • 注意如果在run方法中发生了异常而没有被捕获处理时,那么此线程就会自动结束
  • 所以上面的代码是有问题的,只要当TimerTask中run方法在get连接服务器时发生错误,则会抛出异常,从而直接导致Timer定时器线程结束,也就意味着定时器无法再运行
  • 这种情况执行建议使用ScheduledExecutorService,可以参考《ScheduledExecutorService 详解1》

Timer 常用API

  • public class Timer extends Object:一种工具,线程用其安排以后在后台线程中执行的任务。可安排任务执行一次,或者定期重复执行。 
  • public Timer() :创建一个新计时器,相关的线程不作为守护程序运行,即主线程死掉之后,此线程还是继续执行,应用不会关闭。
  • public Timer(boolean isDaemon):创建一个新计时器,可以指定其相关的线程作为守护程序运行。isDaemon - 如果应该将相关的线程作为守护程序运行,则为 true。
  • public Timer(String name):创建一个新计时器,其相关的线程具有指定的名称。相关的线程不作为守护程序运行。 name - 相关线程的名称。 
  • public Timer(String name,boolean isDaemon):创建一个新计时器,其相关的线程具有指定的名称,并且可以指定作为守护程序运行。 
  • public void cancel():终止此计时器,丢弃所有当前已安排的任务。这不会干扰当前正在执行的任务(如果存在)。一旦终止了计时器,那么它的执行线程也会终止,并且无法根据它安排更多的任务。 注意,在此计时器调用的计时器任务的 run 方法内调用此方法,就可以绝对确保正在执行的任务是此计时器所执行的最后一个任务。可以重复调用此方法;但是第二次和后续调用无效。 
  • public void schedule(TimerTask task,Date time):安排在指定的时间去执行指定的任务。如果此时间已过去,则安排立即执行该任务。 task - 所要安排的任务;time - 执行任务的时间。 
  • public void schedule(TimerTask task, Date firstTime,long period):安排指定的任务从指定的时间开始进行重复的延迟执行。task - 所要安排的任务;firstTime - 首次执行任务的时间;period - 执行各后续任务之间的时间间隔,单位是毫秒。
  • public void schedule(TimerTask task, long delay):安排在指定延迟后执行指定的任务。 
  • public void schedule(TimerTask task,  long delay, long period):安排指定的任务从指定的延迟后开始进行重复的固定延迟执行。以近似固定的时间间隔(由指定的周期分隔)进行后续执行。task - 所要安排的任务;delay - 执行任务前的延迟时间,单位是毫秒;period - 执行各后续任务之间的时间间隔,单位是毫秒。

TimerTask 常用API

  • public abstract class TimerTaskextends Object implements Runnable:由 Timer 安排为一次执行或重复执行的任务。 
  • public boolean cancel():取消此计时器任务。如果任务安排为一次执行且还未运行,或者尚未安排,则永远不会运行。如果任务安排为重复执行,则永远不会再运行。(如果发生此调用时任务正在运行,则任务将运行完,但永远不会再运行。) 
    注意,从重复的计时器任务的 run 方法中调用此方法绝对保证计时器任务不会再运行。 此方法可以反复调用;第二次和以后的调用无效。
  • public abstract void run():此计时器任务要执行的操作,继承TimerTask之后必须实现的方法。 



猜你喜欢

转载自blog.csdn.net/wangmx1993328/article/details/80785614