Нужно исправить мой код таймера Java

Задача - Включать и выключать лампочку в указанное время в течение дня. Мне нужно знать, как исправить мой код в соответствии с информацией, приведенной ниже. Мне также нужно знать, правильно ли я использую класс таймера, то есть правильно ли разработан мой код? Код может работать, но это может быть плохой дизайн, который позже вызовет проблемы. Я не хочу, чтобы это произошло.

Вывод (это не тот результат, который я действительно хотел :() -

This is the main program
Current time is - xxx
Future time is - xxx+5sec
Future time is - xxx+10sec
Main program ends
Bulb B1 is OFF

Желаемый результат -

This is the main program
Current time is - xxx
Future time is - xxx+5sec
Future time is - xxx+10sec
Bulb B1 is ON  //first on
Bulb B1 is OFF //then off
Main program ends//This should always be in the end.

Как мне исправить приведенный ниже код, чтобы получить то, что я хочу?

Bulb Класс

class Bulb {

private boolean state = false;//On or off
private String name;

Bulb(String name){

    this.name = name;

}

public void setState(boolean state){

    this.state = state;
    if(this.state == true){

        System.out.println("Bulb " + name + " is ON");

    }else{

        System.out.println("Bulb " + name + " is OFF");

    }

}


public boolean getState(){
    return this.state;

}


}

BulbJob класс, который является TimerTask

import java.util.*;

class BulbJob extends TimerTask{

private Bulb bulbToHandle;
private boolean setBulbStateEqualTo;

BulbJob(Bulb toHandle){

    this.bulbToHandle = toHandle;

}


//NOTE: Must be called before run(), otherwise default value is used
public void setBulbStateEqualTo(boolean setBulbStateEqualTo){

    this.setBulbStateEqualTo = setBulbStateEqualTo;

}


//NOTE: call run() only before calling above method
public void run(){

    this.bulbToHandle.setState(setBulbStateEqualTo);//Set on or off

}

}

BulbScheduler класс - это расписание, когда лампочка включается или выключается.

import java.util.*;

@SuppressWarnings( "deprecation" )
class BulbScheduler {

public static void main(String args[]) throws InterruptedException{

    System.out.println("This is the main program");

    Timer time = new Timer();
    Bulb b1 = new Bulb("B1");
    BulbJob bj = new BulbJob(b1);

    bj.setBulbStateEqualTo(true);//Task - Turn bulb on at time = afterCurrent

    Date current = new Date();//Get current time and execute job ten seconds after this time
    Date afterCurrent = (Date) current.clone();

    System.out.println("Current time is - " + current);

    int currentSecs = current.getSeconds();
    int offset = 5;//number of seconds

    afterCurrent.setSeconds(currentSecs + offset);
    System.out.println("Future time is - " + afterCurrent);

    time.schedule(bj, afterCurrent);//Schedule job "bj" at time = afterCurrent

    //Now turn the bulb off at new time = newest afterTime
    afterCurrent.setSeconds(currentSecs + 2 * offset);
    System.out.println("Future time is - " + afterCurrent);

    bj.setBulbStateEqualTo(false);//Task - Now turn the bulb off at time = afterCurrent

    System.out.println("Main program ends");

}

}

person Time    schedule 11.03.2013    source источник
comment
+1 за выбор TimerTask вместо обычных Thread sleep().   -  person asgs    schedule 11.03.2013
comment
@asgs - как мне сделать так, чтобы основная программа заканчивалась только после того, как все будет выполнено?   -  person Time    schedule 11.03.2013
comment
Поскольку основной поток не зависит от ваших заданий, я боюсь, что вам придется изменить дизайн, чтобы вы могли использовать метод Thread join() для ожидания завершения TimerTask.   -  person asgs    schedule 11.03.2013
comment
@asgs - Итак, BulbJob расширяет поток??? Но тогда я потеряю всю функциональность TimerTask. Как мне это исправить? :(   -  person Time    schedule 11.03.2013
comment
@asgs - Кроме того, это то, что я хочу сделать - включить лампочку в текущее время + 5 секунд. Затем выключите ту же лампочку в течение текущего времени + 10 секунд. Нужно ли создавать новый BulbJob или таймер, чтобы отключить его?   -  person Time    schedule 11.03.2013
comment
Date.setSeconds не работает должным образом. используйте calender.roll и распечатайте время выполнения задачи. см. любое из двух моих решений ниже   -  person tgkprog    schedule 12.03.2013


Ответы (4)


Эта секция:

time.schedule(bj, afterCurrent);//Schedule job "bj" at time = afterCurrent

//Now turn the bulb off at new time = newest afterTime
afterCurrent.setSeconds(currentSecs + 2 * offset);

назначает только одну задачу. Если вам нужно запланировать его дважды, сделайте это явно:

time.schedule(bj, afterCurrent);//Schedule job "bj" at time = afterCurrent

//Now turn the bulb off at new time = newest afterTime
afterCurrent.setSeconds(currentSecs + 2 * offset);
time.schedule(bj, afterCurrent);//Schedule job "bj" at time = afterCurrent

Также. эта строка:

bj.setBulbStateEqualTo(false);

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

person assylias    schedule 11.03.2013
comment
Кроме того, это то, что я хочу сделать - включить лампочку в текущее время + 5 секунд. Затем выключите ту же лампочку в течение текущего времени + 10 секунд. Нужно ли создавать новый BulbJob или таймер, чтобы отключить его? - person Time; 11.03.2013
comment
Создайте 3 задачи: TurnBulbOn, TurnBulbOff, ExitProgram, которые делают эти вещи, и запланируйте их одну за другой. Вам нужен только один таймер. - person assylias; 11.03.2013
comment
Ну вот. Лучше всего сделать так, чтобы программа вышла с вашим пользовательским сообщением в виде отдельного TimerTask. - person asgs; 11.03.2013
comment
@assylias - как сделать задачу ExitProgram? все остальное работает по плану. Я добавил измененный код в качестве своего ответа. - person Time; 11.03.2013
comment
только отчасти верно. setSecond - это не выход. попробуйте с датой, начинающейся с 2:59:59, а не с 3:00:00. См. другие ответы, используйте Calendar.roll, см. API javadoc java.util.Date#setSecond - person tgkprog; 11.03.2013

Код исправлен, но эта версия не может выйти из main в конце -

import java.util.*;

@SuppressWarnings( "deprecation" )
class BulbScheduler {

public static void main(String args[]) throws InterruptedException{

    System.out.println("This is the main program");

    Timer timeOn = new Timer();
    Timer timeOff = new Timer();
    Bulb b1 = new Bulb("B1");
    BulbJob bjOn = new BulbJob(b1);
    BulbJob bjOff = new BulbJob(b1);

    bjOn.setBulbStateEqualTo(true);//Task - Turn bulb on 
    bjOff.setBulbStateEqualTo(false);//Task - Then turn the bulb off later

    Date current = new Date();//Get current time and execute job ten seconds after this time
    Date afterCurrent = (Date) current.clone();

    System.out.println("Current time is - " + current);

    int currentSecs = current.getSeconds();
    int offset = 3;//number of seconds

    afterCurrent.setSeconds(currentSecs + offset);
    System.out.println("Future time is - " + afterCurrent);

    timeOn.schedule(bjOn, afterCurrent);//Schedule job "bj" at time = afterCurrent

    //Now turn the bulb off at new time = latest afterCurrent
    afterCurrent.setSeconds(currentSecs + 2 * offset);
    System.out.println("Future time is - " + afterCurrent);

    timeOff.schedule(bjOff, afterCurrent);

    System.out.println("Main program ends");

}

}
person Time    schedule 11.03.2013
comment
Вы печатаете Main program ends в основном потоке - он выполнится сразу... Если вы хотите, чтобы он откладывался, вам нужно сделать с ним задачу и запускать ее после выключения лампочки. - person assylias; 11.03.2013
comment
@assylias - хорошо. Я мог бы сделать задачу EnProgram. Но мне придется запускать его только после bjOff. Для этого я должен отслеживать все тайминги, что очень неудобно. К сожалению, для TimerTask нет метода join(), т.е. завершить задачу1 только после завершения задачи2. - person Time; 11.03.2013
comment
Вы также можете использовать механизм блокировки, например CountdownLatch. - person assylias; 11.03.2013
comment
это плохая реализация. setSecond - это не выход. попробуйте с датой, начинающейся с 2:59:59, а не с 3:00:00. Смотрите другие ответы, используйте Calendar.roll - person tgkprog; 11.03.2013

вы не правильно устанавливаете время. Необходимо использовать GreogarianCalendar.

java.util.Date используется, но не может использовать его setSeconds Прочтите Javadoc, это очень хорошо и очень поможет. public void setSeconds (целые секунды)

Устарело. Начиная с JDK версии 1.1, заменено на Calendar.set(Calendar.SECOND, int секунд). Устанавливает секунды этой даты в указанное значение. Этот объект Date изменен таким образом, что он представляет момент времени в пределах указанной секунды минуты, с тем же годом, месяцем, датой, часом и минутой, что и раньше, как интерпретируется в местном часовом поясе.

Вам нужно использовать java.util.GregorianCalendar # add(Calendar.SECOND, howManySeconds)

затем используйте getDate(), чтобы получить объект Date и отправить его на таймер.

вызов setSecond на дату не изменит другие поля. см. java-документ Calendar.add и сверните. http://docs.oracle.com/javase/1.5.0/docs/api/java/util/Calendar.html и ознакомьтесь с правилами во вступительной части класса.

person tgkprog    schedule 11.03.2013

Также можно использовать расписание объекта таймера (задача TimerTask, большая задержка). Планирует выполнение указанной задачи после указанной задержки (в миллисекундах). Модифицированный код -

import java.util.*;

class BulbScheduler {

    private static java.text.SimpleDateFormat sdf1 = new java.text.SimpleDateFormat ("yy MM dd HH mm ss");

//helper    
static String formatDate(Date d){
        return sdf1.format(d);
    }

    public static void main(String args[]) throws InterruptedException{
        System.out.println("This is the main method");
        java.util.GregorianCalendar cal = new java.util.GregorianCalendar();

        Bulb b1 = new Bulb("bulb 1", false);
        Bulb b2 = new Bulb("bulb 2", false);

        System.out.println("Time now " + formatDate(cal.getTime()));

        Timer timer = new Timer("bulbs");
        BulbJob b1On = new BulbJob(b1, true);
        BulbJob b1Off = new BulbJob(b1, false);
        BulbJob b2On = new BulbJob(b2, true);
        BulbJob b2Off = new BulbJob(b2, false);
        timer.schedule(b1On, 3 * 1000);//after 3 seconds
        timer.schedule(b2On, 7 * 1000);//after 4 seconds
        timer.schedule(b1Off, 6 * 1000);//after 6 seconds; before b2 on

        b1On = new BulbJob(b1, true);
        timer.schedule(b1On, 9 * 1000);


        //if you want main to wait need to add code here to make it wait,
        // but even if does the JVM wont exit. Its just a method. The JVM exits when all non daemon threads are done
        // or System.exit is called

        System.out.println("This is the main method ending; but other threads might be running ...");
        //main thread JVM waits for all other non dameons to end

    }

}

Изменено задание на лампочку

импортировать java.util.*;

класс BulbJob расширяет TimerTask{

private Bulb bulbToHandle;
private boolean bulbNewState;//dont start propert names with set

//why a seperate property when we need to set the new state everytime and cannot reuse jobs?
BulbJob(Bulb toHandle, boolean newState){
    this.bulbToHandle = toHandle;
    bulbNewState= newState;
}

public void run(){
    this.bulbToHandle.setState(bulbNewState);//Set on or off
}

}

класс Bulb ... public void setState (логическое состояние) { this.state = state; System.out.println("Bulb" + name + " is " + (state ? "on" : "off") + "at" + BulbScheduler.formatDate(new java.util.Date()));//if хорошо тоже

}

person tgkprog    schedule 11.03.2013