Java Timer.cancel () не отменяет таймер

У меня есть простой сервер сокетов и клиент, настроенный на Java. Я хочу реализовать метод, который проверяет тайм-аут соединения. Обычно клиент отправляет серверу 01101011 01100101 01100101 01110000 каждые 5 секунд. Затем сервер проверяет, установлено ли уже соединение с клиентом, от которого он получил строку.

В противном случае он добавляет IP-адрес клиента в массив с именем connections и запускает 30-секундный таймер. Если 30-секундный таймер достигнет конца, он удалит клиента из массива connections.

В противном случае, если у него уже установлено соединение с клиентом, он отменяет 30-секундный таймер и перезапускает его.

Это мой код:

//This if statement fires every 5 seconds when it received the string from the client
if(o.equals("01101011 01100101 01100101 01110000")) {
    Timer timeoutTimer = new Timer();

    if(Arrays.asList(connections).contains(connection.getOtherEnd().getAddress())) {
        timeoutTimer.cancel();
        timeoutTimer.purge();
        timeoutTimer = new Timer();
    }
    else {
        connections = ArrayModification.append(connections, connection.getOtherEnd().getAddress());
        System.out.println("Client connected");
    }
    timeoutTimer.schedule(new TimerTask() {
        @Override
        public void run() {
            connections = ArrayModification.remove(connections, connection.getOtherEnd().getAddress());
            System.out.println("Client disconnected");
        }
    }, 30000);
}

Он работает нормально около 30 секунд, но затем по какой-то причине выполняет задачи таймера, которые должны были быть отменены.

Вот мой результат:

Client connected <- Happens when the client first connects
Client disconnected <- Happens 30 seconds later from the timer task that should've been cancelled
Client connected
Client disconnected
Client connected
Client disconnected
Client connected

Я не мог понять, почему отмененные таймеры все еще работают. Любая помощь будет оценена по достоинству!


person Denes Garda    schedule 29.05.2021    source источник
comment
Каждый раз, когда выполняется инструкция if, вы создаете новый Timer, чтобы отменить только что созданный новый экземпляр, а не тот, который мог быть создан ранее.   -  person MadProgrammer    schedule 30.05.2021
comment
@MadProgrammer на самом деле наоборот: старый таймер отменяется, а новый - нет.   -  person Turing85    schedule 30.05.2021
comment
Замечание: код можно упростить, переместив создание таймера и -start в ветвь else, а затем исключив ветвь if путем отрицания условия.   -  person Turing85    schedule 30.05.2021
comment
@ Turing85 Timer timeoutTimer = new Timer(); затеняет все остальное, что может быть объявлено глобально, поэтому, когда они вызывают timeoutTimer.cancel, они отменяют экземпляр NEW, а не любой из ранее созданных. Затем они создают ДРУГОЙ экземпляр (так что теперь их два). Я также нигде не вижу, чтобы они создавали эту ссылку, чтобы использовать ее позже. Так что прямо сейчас они не отменяют ранее созданный таймер, так как у них НЕТ ссылки на него - так что это просто беспорядок.   -  person MadProgrammer    schedule 30.05.2021


Ответы (3)


Так что вы

  • Создайте новый экземпляр Timer
  • Если if условие равно true, вы отменяете этот экземпляр и создаете другой.
  • Затем вы планируете задачу, но не поддерживаете ссылку на созданный экземпляр, поэтому нет возможности снова ссылаться на него, чтобы отменить его ...?

введите описание изображения здесь

Итак, в конце метода вы ВСЕГДА создаете и планируете ДРУГОЕ Timer, на которое у вас нет ссылки для отмены в будущем.

Проблема в том, что я думаю, что вам нужно Timer за соединение. Это наводит на мысль, что вы, возможно, захотите связать таймер с самим объектом подключения, но предоставленный вами код находится вне контекста, поэтому трудно напрямую определить, какое может быть подходящее решение.

person MadProgrammer    schedule 29.05.2021
comment
Спасибо! Это помогло мне решить проблему! - person Denes Garda; 30.05.2021

Похоже, вы заменили timeoutTimer другим экземпляром Timer. Таким образом, вы не вызываете cancel() в своем первоначальном Timer, вы запланировали TimerTask, вы вызываете его по только что созданному таймеру.

person asbachb    schedule 29.05.2021
comment
На самом деле все наоборот: старый таймер отменяется, а новый - нет. - person Turing85; 30.05.2021
comment
Поскольку первый оператор Timer timeoutTimer = new Timer();, я не понимаю, как следует вообще отменить первый таймер с тем фрагментом, который вы опубликовали. Откуда вы знаете, что это старый / новый таймер? - person asbachb; 30.05.2021
comment
старый таймер - это тот, который был создан во внешней ветке if. Новый таймер - это тот, который создан во внутренней if-ветке. - person Turing85; 30.05.2021
comment
Да, но вы отменяете безусловно созданное время, прежде чем его можно будет отменить. - person asbachb; 30.05.2021
comment
Внешний таймер отменяется (и согласно API) отклоняет будущие запросы. Однако затем локальным переменным присваивается новый Timer, который не отменяется, поэтому будущая отправка будет выполнена. - person Turing85; 30.05.2021
comment
Как вы все равно должны отменить Timer, когда это локальная переменная, существующая только для одного пинга. - person asbachb; 30.05.2021

Я понял, как это сделать. С помощью комментаторов я понял, что каждый раз создаю новый таймер, а не отменяю старый. Вот как я это исправил.

Сначала я создал объект под названием Timeout. Вот как это выглядит:

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

public class Timeout {
    String address;
    Timer timer;
    TimerTask timerTask;

    protected Timeout(String address) {
        this.address = address;
        this.timer = new Timer();
    }

    public void setTimerTask(TimerTask timerTask) {
        this.timerTask = timerTask;
    }

    public String getAddress() {
        return address;
    }

    public void startTimer() {
        timer.cancel();
        timer.purge();
        timer = new Timer();
        timer.schedule(timerTask, 30000);
    }
}

Затем я изменил код на сервере на этот:

if(o.equals("01101011 01100101 01100101 01110000")) {
    boolean contains = false;
    for (Timeout timeout : connections) {
        if (timeout.getAddress().equals(connection.getOtherEnd().getAddress())) {
            contains = true;
            timeout.setTimerTask(new TimerTask() {
                @Override
                public void run() {
                    connections = ArrayModification.remove(connections, timeout);
                    System.out.println("Client disconnected");
                }
            });
            timeout.startTimer();
            break;
        }
    }
    if(!contains) {
        Timeout timeout = new Timeout(connection.getOtherEnd().getAddress());
        timeout.setTimerTask(new TimerTask() {
            @Override
            public void run() {
                connections = ArrayModification.remove(connections, timeout);
                System.out.println("Client disconnected");
            }
        });
        connections = ArrayModification.append(connections, timeout);
        System.out.println("Client connected");
    }
}

С этой новой настройкой все работает так, как должно!

person Denes Garda    schedule 29.05.2021