Модераторы: LSD, AntonSaburov

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> синхронизация потоков 
:(
    Опции темы
lavan
Дата 6.7.2012, 12:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 101
Регистрация: 21.4.2011

Репутация: нет
Всего: нет



Столкнулся с непонятной проблемой.Хочу решить задачу типа два потока заполняют буфер по очереди.
Сначала поток А выводит символ "А" затем поток Б символ "Б".Ловлю дедлок,при первом же проходе по циклу.
Вставил пару предложений для проверки,получается что компилятор(netbeans) оптимизирует volatile переменную?
Суть алгоритма такова:Если поток А находится в синхронизирующем блоке А то поток Б засыпает. Если поток А 
заходит в синхр блок то он будит поток Б.На выходе получаю
[A] A osv B A wait -1 B wait -0
Получается поток А первый проходит все мониторы,а поток Б ждет освобожд монитора Б.Поток А меняет флаги в
мониторе Б давая тем самым потоку Б отработать выходит из монитора Б.Поток Б наконец то попадает в монитор Б
сейчас уже флагБ==ложь и поток Б не должен уснуть по wait! Но он засыпает и получается дедлок.
Все переменные типа volatile.Такое может происходить,если два потока находились в одном мониторе одновременно
что по идее не возможно!или компилятор берет старое значения флага!Вопрос,что не так?Где я ошибся??
Да,и еще компилятор выдает предупреждение "синхронизация по не полному полю" что это значит?
Код

class A extends Thread{
    private volatile Boolean snA;
    private volatile Boolean snB;
    private volatile Boolean flagA,flagB;
    
    public A(Boolean b,Boolean bb,Boolean _sn,Boolean fb){
        snA=b;
        flagA=bb;
        snB=_sn;
        flagB=fb;
    }
    @Override
    public void run(){
        try{
        for(int i=0;i<10;i++){
        synchronized(snA){//          синхронизация по не полному полю
             if(!flagB){
                 System.out.print("A wait -"+i+" ");
                 snA.wait();
             }
             System.out.print("[A] ");
        }//sn
        synchronized(snB){
            System.out.print("A osv B ");
            flagA=false;
            flagB=false;
                snB.notify();
        }
      }
        }catch(InterruptedException ie){
            System.out.println(ie.getMessage());
        }
    }
}
class B extends Thread{
    private volatile Boolean snA,snB;
    private volatile Boolean flagA,flagB;
    
    public B(Boolean b,Boolean bb,Boolean s,Boolean fl){
        snA=b;
        snB=s;
        flagA=bb;
        flagB=fl;
    }
    @Override
    public void run(){
       try{
        for(int i=0;i<10;i++){
          synchronized(snB){
                if(flagA){
                    System.out.print("B wait -"+i+" ");
                    snB.wait();
                }
                System.out.print("[B] ");
       }
       synchronized(snA){
           System.out.print("B osv A");
           flagA=true;
            flagB=true;
                snA.notify();
          }//sn
        }
      }catch(InterruptedException ie){
          System.out.println(ie.getMessage());
      }
    }
}

public class Main {

    public static void main(String[] args) {
        try {
            Boolean snA=true,snB=true,fl=true,fll=true;
            A ta=new A(snA,fl,snB,fll);
            B tb=new B(snA,fl,snB,fll);
            ta.start();
            tb.start();
            ta.join();
            tb.join();
        } catch (InterruptedException ex) {
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
        }

    }
    }

PM MAIL   Вверх
LSD
Дата 6.7.2012, 14:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Leprechaun Software Developer
****


Профиль
Группа: Модератор
Сообщений: 15718
Регистрация: 24.3.2004
Где: Dublin

Репутация: 210
Всего: 538



У объекта Boolean есть два константы TRUE и FALSE и если использовать автовайринг, то будут создавать не новые объекты а использоваться эти две константы. Так что snA, snB, fl, fll инициализируются одним и тем же объектом.

Создай отдельные объекты на которых и синхронизируйся.


--------------------
Disclaimer: this post contains explicit depictions of personal opinion. So, if it sounds sarcastic, don't take it seriously. If it sounds dangerous, do not try this at home or at all. And if it offends you, just don't read it.
PM MAIL WWW   Вверх
Pawl
Дата 6.7.2012, 14:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 649
Регистрация: 22.4.2008
Где: Витебск

Репутация: 7
Всего: 28



Проблем в том, что тут у Вас получаются 2 разных объекта, каждый со своим набором полей, когда Вы пишете 
Код

            flagA=false;
            flagB=false;
 для объекта класса А, это коснется только переменных данного класса. В объекте класса В они все-равно останутся true. Вам надо каким-то образом из объекта класса А менять значения переменных объекта класса В и наоборот. Использовать, например, сеттеры. А еще проще написать в main-методе так:
Код

    public static void main(String[] args) {
        try {
            Boolean snA=true,snB=true,fl=true,fll=true;
            A ta=new A(snA,fl,snB,fll);
            A tb=new A(snA,fl,snB,fll);
            ta.start();
            tb.start();
            ta.join();
            tb.join();
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }
 
Тогда получится, что оба потока меняют значение переменных одного объекта, и будет Вам счастье:)


--------------------
В действительности всё совсем не так, как на самом деле
PM MAIL   Вверх
lavan
Дата 6.7.2012, 14:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 101
Регистрация: 21.4.2011

Репутация: нет
Всего: нет



а разве синхронизация не должна происходить на одном и том же объкте синхронизации?
PM MAIL   Вверх
Pawl
Дата 6.7.2012, 15:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 649
Регистрация: 22.4.2008
Где: Витебск

Репутация: 7
Всего: 28



Цитата(lavan @  6.7.2012,  14:55 Найти цитируемый пост)
а разве синхронизация не должна происходить на одном и том же объкте синхронизации

Ну да, первый поток захватывает монитор объекта, методом wait отправляет другой поток покурить, а сам в это время что-то с этим объектом делает. Затем зовет второй поток методом notify и сам уходит курить. и с этим же объектом работает уже другой поток. А у Вас получается следующее:
Код

A ta=new A(snA,fl,snB,fll);
ta.start();

первый поток работает с объектом класса A
Код

B tb=new B(snA,fl,snB,fll);
tb.start();

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


--------------------
В действительности всё совсем не так, как на самом деле
PM MAIL   Вверх
lavan
Дата 6.7.2012, 15:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 101
Регистрация: 21.4.2011

Репутация: нет
Всего: нет



Цитата(Pawl @ 6.7.2012,  14:49)
Проблем в том, что тут у Вас получаются 2 разных объекта, каждый со своим набором полей, когда Вы пишете 
Код

            flagA=false;
            flagB=false;
 для объекта класса А, это коснется только переменных данного класса. В объекте класса В они все-равно останутся true

Не понял,я создаю объект класса Boolean,передаю его по ссылке и делаю volatile.Вроде он должен изменяться?
PM MAIL   Вверх
LSD
Дата 6.7.2012, 15:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Leprechaun Software Developer
****


Профиль
Группа: Модератор
Сообщений: 15718
Регистрация: 24.3.2004
Где: Dublin

Репутация: 210
Всего: 538



Цитата(Pawl @  6.7.2012,  16:06 Найти цитируемый пост)
Эти потоки не пересекаются друг с другом, поскольку работают каждый на своем объекте. Поэтому синхронизация у Вас тут как раз на разных объектах. 

А дедлок откуда?


--------------------
Disclaimer: this post contains explicit depictions of personal opinion. So, if it sounds sarcastic, don't take it seriously. If it sounds dangerous, do not try this at home or at all. And if it offends you, just don't read it.
PM MAIL WWW   Вверх
Pawl
Дата 6.7.2012, 15:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 649
Регистрация: 22.4.2008
Где: Витебск

Репутация: 7
Всего: 28



Цитата(lavan @  6.7.2012,  15:09 Найти цитируемый пост)
Вроде он должен изменяться

Ну, получается, что нет. я тут даже набросал следующий код:
Код

public class TestBool {
    static class A {
        Boolean aA;
        
        public A(Boolean a) {
            aA = a;
        }
        
        public void setA() {
            aA = false;
        }
        
        public void print() {
            System.out.println(aA.toString());
        }
    }
    
    static class B {
        Boolean aB;
        
        public B(Boolean b) {
            aB = b;
        }
        
        public void print() {
            System.out.println(aB.toString());
        }
    }
    
    public static void main(String[] args) {
        Boolean aC = true;
        A a = new A(aC);
        B b = new B(aC);
        a.setA();
        b.print();
    }
}

так вот, после вызова setA на объекте а, поле aB все-равно осталось true.

Добавлено @ 15:58
Цитата(LSD @  6.7.2012,  15:45 Найти цитируемый пост)
А дедлок откуда?

Ну как же, сначала первый поток отправляется курить на объекте класса А:
Код

snA.wait();

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

snB.wait();
 - вот и деадлок!

Это сообщение отредактировал(а) Pawl - 6.7.2012, 15:59


--------------------
В действительности всё совсем не так, как на самом деле
PM MAIL   Вверх
lavan
Дата 6.7.2012, 16:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 101
Регистрация: 21.4.2011

Репутация: нет
Всего: нет



Ну хорошо,если каждый поток имеет свой объект синхр,то почему иногда они все таки отрабатывают правильно?
PM MAIL   Вверх
Pawl
Дата 6.7.2012, 16:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 649
Регистрация: 22.4.2008
Где: Витебск

Репутация: 7
Всего: 28



Цитата(lavan @  6.7.2012,  16:03 Найти цитируемый пост)
Ну хорошо,если каждый поток имеет свой объект синхр,то почему иногда они все таки отрабатывают правильно?

???! Во как! Тогда не знаю, надо подумать... 


--------------------
В действительности всё совсем не так, как на самом деле
PM MAIL   Вверх
jk1
Дата 6.7.2012, 18:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник
Сообщений: 1168
Регистрация: 17.10.2008
Где: Санкт-Петербург

Репутация: 40
Всего: 75



Цитата

Ну хорошо,если каждый поток имеет свой объект синхр,то почему иногда они все таки отрабатывают правильно? 


Прогнал у себя, действительно 1-2 запуска из 10 завершаются успешно. Race condition как он есть.
Потом заменил
Код

if  (flagA){
      System.out.print("B wait -"+i+" ");
      snB.wait();
}

на
Код

while (flagA){
      System.out.print("B wait -"+i+" ");
      snB.wait();
}

для обоих потоков, как и положено при работе с wait(). После этого за пару тысяч запусков ни один успешно не отработал.
Из всего этого следует вывод, что дело в вероятности потока самопроизвольно просыпаться из wait(). Если он действительно проснулся сам по себе без notify(), то программа завершается. Если нет, то стоит залоченная.

P.S.: Синхронизация тут все-таки на одинаковых объектах, snA и snB внутри потоков не присваиваются, а вот флаги расходятся кто куда

P.P.S: Интересный эффект получается при использовании вот такой комбинации начальных данных
Код

 Boolean snA=true,snB=true,fl=false,fll=false;

теперь уже большинство запусков успешны, а отдельные - падают


--------------------
Opinions are like assholes — everybody has one
PM MAIL   Вверх
Pawl
Дата 6.7.2012, 19:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 649
Регистрация: 22.4.2008
Где: Витебск

Репутация: 7
Всего: 28



Цитата(jk1 @  6.7.2012,  18:13 Найти цитируемый пост)
Синхронизация тут все-таки на одинаковых объектах, snA и snB

Извините, не могли бы Вы обосновать, почему на одинаковых? Ведь snA и snB имеются как у объекта класса А, так и у В, и изменение значения того же snA на одном объекте никак не затронет значение snA на другом?


--------------------
В действительности всё совсем не так, как на самом деле
PM MAIL   Вверх
jk1
Дата 6.7.2012, 23:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник
Сообщений: 1168
Регистрация: 17.10.2008
Где: Санкт-Петербург

Репутация: 40
Всего: 75



Цитата

Извините, не могли бы Вы обосновать, почему на одинаковых? 


Потому что внутри классов A и B не выполняется присвоения snA и snB, на которых происходит синхронизация, кроме как в конструкторе.
А в конструктор передали одну и ту же пару объектов. 

Присваиваются только флаги, на которых синхронизация не делается.


--------------------
Opinions are like assholes — everybody has one
PM MAIL   Вверх
Pawl
Дата 7.7.2012, 00:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 649
Регистрация: 22.4.2008
Где: Витебск

Репутация: 7
Всего: 28



Цитата(jk1 @  6.7.2012,  23:07 Найти цитируемый пост)
А в конструктор передали одну и ту же пару объектов. 

То есть, Вы хотите сказать, что в объектах классов А и В изменяются не локальные копии полей, а именно те объекты Boolean, которые передались в конструктор?

Это сообщение отредактировал(а) Pawl - 7.7.2012, 00:19


--------------------
В действительности всё совсем не так, как на самом деле
PM MAIL   Вверх
jk1
Дата 7.7.2012, 00:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник
Сообщений: 1168
Регистрация: 17.10.2008
Где: Санкт-Петербург

Репутация: 40
Всего: 75



Цитата

То есть, Вы хотите сказать, что в объектах классов А и В изменяются не локальные копии полей


Нет, я хочу сказать, что snA и snB там не изменяются. Вообще. Их можно final сделать.
Перечитайте пожалуйста внимательно код из первого поста.


--------------------
Opinions are like assholes — everybody has one
PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Java"
LSD   AntonSaburov
powerOn   tux
javastic
  • Прежде, чем задать вопрос, прочтите это!
  • Книги по Java собираются здесь.
  • Документация и ресурсы по Java находятся здесь.
  • Используйте теги [code=java][/code] для подсветки кода. Используйтe чекбокс "транслит", если у Вас нет русских шрифтов.
  • Помечайте свой вопрос как решённый, если на него получен ответ. Ссылка "Пометить как решённый" находится над первым постом.
  • Действия модераторов можно обсудить здесь.
  • FAQ раздела лежит здесь.

Если Вам помогли, и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, LSD, AntonSaburov, powerOn, tux, javastic.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Java: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0994 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.