Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Java: Общие вопросы > Последовательный запуск Thread


Автор: AbdulBcex 30.6.2010, 18:09
Всем здравствовать!

Наткнулся тут на проблему. Есть класс наследующий класс, который имплементирует Runnable. Делаю @Override метода run();
Код

public void run() {

try{ 
Thread.sleep(OFFSET);
}catch(InterruptedException e1){}

while(condition) //Что бы это значило, кстати?
      while(active){
                 something.doSomethingElse();
                 .........
}
}

Код с комментами взят из метода суперкласса, я его не трогал. Объясните, пожалуйста, что это значит?
А собственно вопрос таков. Я делаю несколько объектов класса. У каждого вызываю метод start(), один за другим. Нужно, чтобы первый объект сразу переходил "к делу" - у него OFFSET = 0, у второго - 25 (50 и 75 у других соответственно). Смотрю на данные логов - ну свистопляска чистой воды! Первые значений 20 выходят практически ровно, разница в сотых долях. Потом такие скачки начинаются, аж жуть. Забавно, но ведь по идее один поток стартует сразу, второй спит 25 мс...
Причем делаю на разных компьютерах, на одном JVM специально выделено отдельное ядро и гигабайт оперативки - всегда разный результат и те же скачки. Попробовал Thread.sleep(OFFSET, 0);, так он мне потом вообще разницу в 7(!) мс вместо 25 выдал. 

Поискал тут на форуме, видел похожие темы, но они либо старые, либо не совсем то. Подскажите, пожалуйста, где поискать-посмотреть-почитать, что ли.

Автор: LSD 30.6.2010, 19:32
1. sleep() не гарантирует что поток будет остановлен ровно на Х мсек. Это время приблизительное. И вообще он может проснутся раньше, смотри документацию.
2. Разрешаюшая способность таймера тоже не 1 мсек, а больше, под виндой это где-то 10 мсек.
3. Нет никаких гарантий на тему того, как система будет переключать потоки. Даже если у тебя N потоков и N ядер, это вовсе не гарнтирует что каждому потоку будет выделенно отдельное ядро и они на самом деле будут выполнятся паралельно. А уж в случае одного ядра, переключение между потоками просто гарантированно. Плюс запись логов задача связанная с блокировками на синхронизацию, обращение к внешним ресурсам (например система IO), что так же вызывает переключение потоков.

Если ты хочешь получить какой-то гарантированный порядок - используй синхронизацию или java.util.concurrent.

Автор: Skipy 30.6.2010, 21:15
Почитайте про синхронизацию: http://www.skipy.ru/technics/synchronization.html

Автор: AbdulBcex 1.7.2010, 15:03
Спасибо за советы.

На самом деле синхронизация действительно единственное, что приходит в голову. Но она требует объект, которого нет, который нужно сделать только для синхронизации. Опять же блоки synchronize и т.д., которые не ясно где ставить, метода main у меня нет, потоки принадлежат разным объектам одного класса, которые между собой никак не взаимодействуют, и не особо радует перспектива это взаимодействие создавать. 

Нужно-то всего лишь притормозить запуск каждого следующего потока на данное количество мс. Я делал до четырех потоков, так несмотря ни на что (на их количество и параллельную работу, например), данные они возвращали четко с заданной переодичностью, то есть data(i) - data(j) = const мс.

В общем, думаю дальше.

Автор: LSD 1.7.2010, 15:45
Так чего тебе все таки надо: организовать паралельную работу нескольких потоков или красиво вывеси в консоль:
Цитата
поток 1
поток 2
поток 3

через заданные интервалы времени?

Если второе то просто используй java.util.Timer.

Автор: AbdulBcex 2.7.2010, 14:05
Да видимо объяснить толково не очень выходит smile .

Есть n потоков. Каждый поток организуется n-ным объектом класса:
Код

obOfClass1.start(); //В методе старт создается поток, если он уже не существует и потом соответственно thread.start();
obOfClass2.start();
...
obOfClassn.start();
 

Каждый следующий поток стартует через OFFSET = n*25 мс (т.е. 0 - сразу, 1 - 25 мс, 2 - 50, 3 - 75 мс и т.д.) начинает давать данные с периодом Т.
OFFSET я задаю через 
Код

run(){
try{//самое начало метода run()
Thread.sleep(OFFSET);
}catch(...){...}
...
...
...


Ну а дальше уже внутри метода есть считалки через System.nanoTime() дающие тот самый период. И все бы было ничего, если б не восьмой этаж и то, что где-то в середине именно считалки дают сбой - т. е. вместо T идет период T+(-)some_time. Такого рода отклонения ловятся с помощью прибавки/убавки времени. Т.е. в следующий раз компенсируется с помощью T-(+)some_time. 
Вот. Такие дела. Выглядит сложновато, наверное, но как по мне, так это проще, чем выдумывать синхронизацию на пустом месте. В принципе, работает. Сейчас свистопляски не наблюдается особой. Более менее приближено к прямой, даже средние значения T лежат в пределах +-10мс, (хотя и то много, но это максимум, что наблюдал до сего времени). 

Еще не видел, что такое java.util.cocurrent, но если про параллельную работу с объектами, то мне как-то не очень.

Автор: LSD 2.7.2010, 16:44
Цитата(AbdulBcex @  2.7.2010,  15:05 Найти цитируемый пост)
Ну а дальше уже внутри метода есть считалки через System.nanoTime() дающие тот самый период. И все бы было ничего, если б не восьмой этаж и то, что где-то в середине именно считалки дают сбой - т. е. вместо T идет период T+(-)some_time. Такого рода отклонения ловятся с помощью прибавки/убавки времени. Т.е. в следующий раз компенсируется с помощью T-(+)some_time. 

А в чем логика приложения то? Поток должен отсчитать N мсек, вывести сообщение и заснуть?

Автор: AbdulBcex 5.7.2010, 11:42
Цитата

А в чем логика приложения то? Поток должен отсчитать N мсек, вывести сообщение и заснуть? 


Не совсем. Поток генерирует данные, раз в 100 мс, с временными отметками (timestamp), каждый - свои (т.е. неодинаковые), а потом передает их дальше (не другим потокам, а дальше по цепочке). Задержка нужна, чтобы более четко и вовремя заполнять стеки приемников данных, поскольку данные, к примеру, первого потока будут идти дольше - их еще обработать нужно, в то время как данные четвертого идут сразу в стек приемника.


В общем, работаю над синхронизацией. Слишком уж нестабильно без нее. Для себя сделал вывод: хочешь организовать параллельную работу даже не имеющих отношения друг к другу потоков - synchronize.

Хотя уже сейчас вижу, что в принципе это нелегко даже с синхронизацией. Стало лучше, но до нормальных (и более менее постоянных) результатов еще далеко.

Автор: AbdulBcex 7.7.2010, 12:47
И снова здравствуйте!!! Извиняюсь, что "бужу" тему.

Подскажите, пожалуйста, как синхронизировать разные потоки, созданные разными объектами одного класса. 
Я слегка замумукался - все примеры идут для синхронизации объекта или метода одного класса, для доступа потоками других классов. 
Мне же нужно, по сути, синхронизировать внутри одного метода run(), выполняемого в последствии каждым из этих потоков.
Вот мой код:
Код

public class MyClass(){

Object sync = new Object();
...
boolean canProceed = true;
volatile int FLAG = 0;
int ID, OFFSET;// См. объяснение задачи выше
...
MyClass(int threadID, int OFFSET){
this.ID = threadID;
}

run(){
try{
Thread.sleep(OFFSET)}catch(...){...}//Каждый поток вначале засыпает на время (у первого OFFSET = 0, у второго - 25 и т.д.)

synchronized(sync){ //Начинается "синхронизированный" (в теории :wacko ) блок
while(!canProceed && ID == FLAG){//Идет проверка и вызов wait(); FLAG по идее должен пускать поток следующий за нынешним
//Изначально FLAG = 0 для первого потока
try{
sync.wait();
}catch(...){...}

canProceed = false;//поток зашел и тут же сменил основной "проходной параметр". 
//Начальная задержка на OFFSET и FLAG = 0 практически гарантирует, что первым будет именно поток с ID = 0 (в теории)
performMainAction();

sync.notify();//Поток заканчивает работу в синхронизированном блоке
canProceed=true;

if(ID == 0) FLAG = 1;//FLAG меняется на следующий
else if(ID == 1) FLAG = 2;
...
else if(ID == n) FLAG = n+1;

}
}


Путем экспериментов выяснил, что
  • Период Т, о котором говорил выше, сохраняется и потоки работают более менее параллельно.
  • Предыдущее утверждение верно не всегда. Если время работы программы более 2-3х минут, например...
  • Цикл while вообще не посещается потоками... Но при этом см. пункт 1.
  •  I need help... smile

Автор: 4EJIOBEK 8.7.2010, 11:13
Ваш класс не описывает интерфейс Runnable  не является наследником Thread. 
Как вы используете свой класс(стартуете его в потоке)?

Автор: AbdulBcex 8.7.2010, 12:43
Я все это еще в первом посте описал вроде  smile. Потом уже так, общие наброски. Да и в принципе, тему можно считать закрытой.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)