timerSchedule не подчиняется установленному таймеру запуска?

У меня есть тестовый файл с тремя строками, каждая строка начинается с метки времени в секундах. Я читаю эту метку времени и на ее основе планирую новый таймер для каждой считываемой метки времени. Почему, когда я запускаю потоки, созданные в методе «processFile», упомянутом ниже, консоль отображает вывод трех таймеров одновременно? Я ожидал увидеть выходные данные каждого таймера, разделенные задержкой в ​​​​несколько секунд, поскольку отметка времени каждой из трех строк, которые я читал из файла, составляет 2 секунды, 15 секунд и 7 секунд соответственно? когда я запускаю программу, через несколько секунд я получаю приведенный ниже вывод «системного времени» сразу без задержки, разделяю их, несмотря на то, что метка времени, считываемая из файла, отличается!

Пожалуйста, дайте мне знать, почему это происходит и как это решить.

//this method is being called on a worker thread

    private static void processFile(File dataFile) throws IOException {
    // TODO Auto-generated method stub
    System.out.println(WORKER_THREAD + " started.");
    logfile = new MeasurementFile(dataFile, MeasurementFile.ENCODING_UTF_8);
    System.out.println("Total lines in the file: " + logfile.getTotalLines());
    System.out.println("#Parameters/line: " + logfile.getFileHash().get(logfile.getTotalLines()).getFullParameters().length);
    System.out.println("Time Stamp in Seconds: " + logfile.getFileHash().get(logfile.getTotalLines()).getTimeStampInSec());

    timer = new Timer();
    for (int i = 1; i <= logfile.getTotalLines(); i++) {
        ++currentLine;
        t = new Thread(new threadFile(), "logFile_Thread_" + i);
        t.start();
    }

}

static class threadFile implements Runnable {
    public void run() {
        timer.schedule(new timedTask(), (long) logfile.getFileHash().get(currentLine).getTimeStampInMilli());
    }
}

static class timedTask extends TimerTask {

    @Override
    public void run() {
        // TODO Auto-generated method stub
        long nanoTimer = System.nanoTime();
        //long milliTimer = System.currentTimeMillis();
        System.out.println("systemTime(ns): " + nanoTimer);
    }
}

Вывод:

file processing thread started.
Total lines in the file: 3
#Parameters/line: 9
Time Stamp in Seconds: 7.0
systemTime(ns): 4882255697670
systemTime(ns): 4882255928644
systemTime(ns): 4882255985104

person rmaik    schedule 05.12.2014    source источник
comment
Я не знаком с MeasurementFile. Как это считывает время с каждой строки, и вы уверены, что это не дает сбой и по умолчанию равно 0?   -  person Rudi Kershaw    schedule 05.12.2014
comment
этот файл представляет собой класс, который обрабатывает файл и анализирует данные построчно, а затем анализирует значения из каждой строки.   -  person rmaik    schedule 05.12.2014
comment
Вероятно, вам следует вывести значение, которое оно берет из каждой строки, чтобы убедиться, что оно правильно обрабатывается. В противном случае я не вижу проблемы с вашим кодом.   -  person Rudi Kershaw    schedule 05.12.2014
comment
@Rudi извините, я не мог понять, что вы имели в виду, вероятно, вы должны вывести значение, которое оно принимает из каждой строки?   -  person rmaik    schedule 05.12.2014
comment
System.out.println(logfile.getFileHash().get(currentLine).getTimeStampInMilli()); Так вы сможете убедиться, что получили ожидаемый результат.   -  person Rudi Kershaw    schedule 05.12.2014


Ответы (2)


Я подозреваю цикл for и счетчик внутри него, а также то, как вы создаете новый экземпляр потока. Я бы сделал так.

for (int i = 1; i <= logfile.getTotalLines(); i++) {
    t = new Thread(threadFile, "logFile_Thread_" + i);
    t.start();
}

static Runnable threadFile = new Runnable() {

    public void run() {
        // TODO Auto-generated method stub
        ++currentLine;
        timer.schedule(new timedTask(), (long)  
        logfile.getFileHash().get(currentLine).getTimeStampInMilli());
    }
};

Поскольку счетчик в цикле for всегда будет получать максимальное значение, которое будет применяться ко всем задачам таймера. Другими словами, если у вас есть цикл для 5 итераций, счетчик получит свое максимальное значение в зависимости от того, как счетчик был инициализирован, а затем значения этого счетчика вы используете для анализа определенных данных из вашего файла, что означает, что вы всегда получаете значение frm последняя строка, затем примените ее к timerTask

person user2121    schedule 05.12.2014
comment
Хорошо подмечено. Я даже не заметил, что он использовал currentLine в различных контекстах и ​​потоках. - person Rudi Kershaw; 05.12.2014
comment
Вы правы, что существует гонка по этой конкретной переменной. Однако ваш метод не решает эту гонку, так как часть ++currentLine не синхронизирована. - person RealSkeptic; 05.12.2014
comment
Пока currentLine больше нигде не увеличивается, вы можете гарантировать отсутствие состояния гонки, добавив приращение в вызов. .get(++currentLine). Тем не менее, вам, вероятно, лучше синхронизироваться. Так меньше шансов сломаться в будущем. - person Rudi Kershaw; 05.12.2014
comment
@ Руди, что ты имеешь в виду, что никто больше не имеет к нему доступа? Было создано три потока, все они запускают этот runnable и увеличивают этот счетчик. - person RealSkeptic; 05.12.2014
comment
@RealSkeptic - я просто имел в виду, что увеличивая во время вызова единственных методов, которые его используют (метод get()), вы можете гарантировать, что он будет правильным во время вызова, пока он изменяется только во время вызовов get(). Это глупый взлом, я, вероятно, не должен был упоминать об этом. - person Rudi Kershaw; 05.12.2014
comment
@Rudi Я думаю, вы совершаете ошибку (или, может быть, я ошибаюсь, поэтому, пожалуйста, поправьте меня), предполагая, что приращение произойдет внутри вызова. Поток достигнет этой строки, выполнит ++currentLine и передаст результат get(). get() вызывается только после того, как будут вычислены все выражения, переданные в его параметры. - person RealSkeptic; 05.12.2014
comment
Я отказываюсь от своего первоначального заявления. У меня сложилось ошибочное впечатление, что JVM выполняет по одной строке за раз. Чего нет. - person Rudi Kershaw; 05.12.2014

Гонку на currentLine я бы решил иначе:

for (int i = 1; i <= logfile.getTotalLines(); i++) {
    t = new Thread(new ThreadFile(i), "logFile_Thread_" + i);
    t.start();
}

Определив ThreadFile таким образом:

static class ThreadFile implements Runnable {

    private int lineToProcess;

    public ThreadFile( int lineToProcess ) {
         super();
         this.lineToProcess = lineToProcess;
    }

    public void run() {
        timer.schedule(new timedTask(), (long) logfile.getFileHash().get(lineToProcess).getTimeStampInMilli());
    }
}

Таким образом, когда вы создаете runnable, он уже знает, к какой строке обращаться, и нет доступа к common переменной и нет необходимости в синхронизации.

С logfile все еще может быть какая-то проблема, так как он тоже общий и не синхронизированный, но если все, что мы делаем, это читаем из него и чтение не связано с обновлением его состояния, все должно быть в порядке.

person RealSkeptic    schedule 05.12.2014