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


Автор: Platon 23.1.2008, 12:01
Здравствуйте, уважаемые.

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

Код

public class TestConnection {
    public boolean hasUser(String user) {
        session.write(new HasUserRequest(user));
        return true; // вот тут то надо дождаться ответа
    }
}


ответные сообщения идут от соединений вот так:

Код

public class MessageHandler {
    public void messageReceived(IoSession session, Object message) {

    }
}


Думал сделать так:

Код

public class TestConnection {
    private static final ResponseWaitor<Boolean> waitor = new ResponseWaitor<Boolean>();

    public boolean hasUser(String user) {
        session.write(new HasUserRequest(user));
        return waitor.getObject();
    }

    public void notifyHasUser(boolean hasUser) {
        waitor.setObject(hasUser);
    }
}


Код

public class MessageHandler {
    public void messageReceived(IoSession session, Object message) {
         if (message instanceof HasUserResponse) {
             connectionsManager.getTestConnection(session).notifyHasUser(((HasUserResponse)message).hasUser);
         }
    }
}


Все бы ничего но для каждого синхронизированного запроса так изголяться?! уж тяжело даже мне. Благо пока 1 такой метод, но если поставить на массовый поток клепание синхронизированых методов?

Я явно упустил какой-то легкий подход.

Автор: Platon 23.1.2008, 13:45
Кстати вот мой объект, ожидающий ответа:
Код

public class ResponseWaitor<E> {
    private E resp;

    public synchronized void setObject(E resp) {
        this.resp = resp;
        notify();
    }

    public boolean isDone() {
        return resp != null;
    }

    public E getObject() {
        while(!isDone()) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return resp;
    }
}


Но, боюсь, что если более 1 запроса будет ожидать обработки, то могут возникнуть разного рода проблемы?
К примеру, в сокет отправилось 2 запроса о проверке наличия пользователя, и как я вижу, 2 потока лягут спать на объекте ResponseWaitor и при обращении к методу ResponseWaitor.notify() неизвестно какой поток пробудится...

Автор: LSD 23.1.2008, 14:11
Я делал так:
- один поток обрабатывал сеть (отправлял и получал оттуда данные)
- другие потоки вызывали методы этого объекта для отправки данных
- когда метод хотел синхронно отправить сообщение, объект работающий с потоком обработки сети отправлял данные, записывал в что ожидается ответ на сообщение с таким-то ID, и усыплял поток
- когда приходило сообщение, проверял что это подтверждение, будил все потоки и у каждого проверял, что это сообщение для него, нужный поток получал управление обратно и ответ, а все остальные снова засыпали дальше

Автор: AntonSaburov 23.1.2008, 14:24
Что-то ты и правда намудрил. Попробуй описать задачу целиком - может найдется просто иное решение. Проще и "стандартнее".

Автор: Platon 23.1.2008, 14:59
LSD, ну на самом деле не намного проще получается :( или я просто не понял.


AntonSaburov, использую библиотеку Apache MINA для организации сети.
Задача: оформить синхронизированную работу функций.
Вот я накидал как оно выглядит
Клиентская часть
http://ipicture.ru/Gallery/Viewfull/379958.html

Серверная часть
http://ipicture.ru/Gallery/Viewfull/379961.html

Главная загвоздка находится в классе ServerConnection, как раз там необходимо усыплять потоки.

Автор: LSD 23.1.2008, 15:41
Цитата(Platon @  23.1.2008,  14:59 Найти цитируемый пост)
ну на самом деле не намного проще получается :( или я просто не понял

Вообщем да. В любом случае проблема состоит в том, что надо усыплять поток до тех пор пока не придёт ответ. Хотя тут код получится не такой уж чтобы сверх сложный.

Автор: Platon 23.1.2008, 16:36
Хм, странно, попробовал потестировать со своим ResponseWaitor'ом тест прошел, глюков и дедлоков не было... надо думать что всё таки так дело нормально пройдет, проверим еще на нескольких тачках.

Автор: COVD 23.1.2008, 17:39
Цитата

К примеру, в сокет отправилось 2 запроса 


Очевидно, вы хотите синхронные запросы пропускать через один канал связи, т.е. через один сокет. Это неестественно. Натурально будет, если для синхронного запроса открыть новое соединение, получить ответ и закрыть соединение. Используя асинхронный канал для синхронных запросов вы получаете свои трудности:
 - иметь коллекцию, где потоки запросов будут ожидать ответа, 
 - иметь таймер, который мониторит коллекцию и выбрасывает потоки, у которых задержка ответа превышает таймаут, 
 - в каждом запросе отправлять уникальный ID запроса
 - в каждом ответе на запрос возвращать ID запроса, чтобы отыскать поток в коллекции и разбудить его.  

Автор: Platon 23.1.2008, 17:48
Цитата(COVD @  23.1.2008,  18:39 Найти цитируемый пост)
Натурально будет, если для синхронного запроса открыть новое соединение, получить ответ и закрыть соединение.

Ну, уж так уж натурально?
видимо с ID придется бегать...

Автор: LSD 23.1.2008, 17:49
Ты главное потоков побольше запусти, и дольше тестируй.

P.S. У меня там ещё тайм аут обрабатывался. А у тебя как с этим? 

Автор: Platon 23.1.2008, 17:59
На самом деле никак. Я еще вечерком подумаю над тем что мне насоветовали ^_^

Автор: COVD 23.1.2008, 18:02
Цитата

Ну, уж так уж натурально?


именно так, потому что не надо синхронную схему приспосабливать к асинхронному каналу связи - никаких ID, коллекций, таймеров и блоков синхронизаций.  

Автор: Platon 23.1.2008, 18:12
COVD, это уже получается сервис? но это соединение также обслуживает асинхронные запросы.

В моем примере на самом деле проблема только с тем кто разбудится. Но 100% Гарантировано, что при записи в поток запросов ответы придут в той же (ненарушенной) последовательности.

Автор: COVD 23.1.2008, 18:54
Цитата

COVD, это уже получается сервис?

фактически, да. Если сиюминутная проблема только в том, "кто разбудится", то кладите потоки (т.е. обьекты, на которых они спят) в ArrayList и в том же порядке извлекайте и будите. 

Автор: Platon 23.1.2008, 19:20
Ок, логичное решение... не догадался.
НО, вот будет утомительно плясать вокруг каждого такого метода...

Каков же опыт RMI, на сколько я знаю там все методы сделаны в синхронном стиле. но нам плясать никак не надо. Может быть этот опыт можно повзаимствовать или придется все методы делать синхронными.

Автор: Platon 24.1.2008, 11:23
Свелось всё к классической задачке ^_^

Код

public class ResponseWaitor<E> {

    private LinkedList<E> queue = new LinkedList<E>();
    private final Object lock = new Object();
    public void add(E obj) {
        synchronized(lock) {
            queue.add(obj);
            lock.notify();
        }
    }
    
    public E get() {
        synchronized(lock) {
            while (queue.isEmpty()) {
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    return null;
                }
            }
            return queue.removeFirst();
        }
        
    }
}

Автор: Platon 24.1.2008, 11:40
И получается не так уж и страшно:

Код

private ResponseWaitor<Boolean> waitor; 

    public boolean addUserToContacts(String sn, String sn1) {
        connection.write(new AddUserRequest(sn, sn1));
        return waitor.get();

    }

    void userAdded(boolean added) {
        waitor.add(added);
    }

Автор: Platon 26.1.2008, 15:23
Нет, вариант оказался неправильным, вот новый, но я даже не рискнул его запустить, просто не укладывается в мозгу как сделать правильно.
Опытные тредеры!!! Нужна консультация.

Код

public class ResponseWaitor<E> {

    private final LinkedList<E> queue = new LinkedList<E>();
    private final LinkedList monitors = new LinkedList();

    public synchronized void add(E obj) {
        Object o;
        synchronized(monitors) {
            while (monitors.isEmpty()) {
                try {
                    monitors.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            o = monitors.removeFirst();
        }
        synchronized(queue) {
            queue.add(obj);
        }
        synchronized(o) {
            o.notify();
        }
    }

    public E get() {
        Object o;
        synchronized(monitors) {
            o = new Object();
            monitors.add(o);
            monitors.notify();
        }

        synchronized(queue) {
            while (queue.isEmpty()) {
                synchronized(o) {
                try {
                    o.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                }
            }
            return queue.removeFirst();
        }
    }
}


Добавлено через 6 минут и 47 секунд
Хм, нет, этот вариант конечно же завален.

Автор: Platon 26.1.2008, 15:49
Вот кажется валидный пример:

Код

public class ResponseWaitor<E> {

    private LinkedList<E> queue = new LinkedList<E>();
    private LinkedList monitors = new LinkedList();
    private final Object lock = new Object();
    public void add(E obj) {
        Object o;
        synchronized(lock) {
            while (monitors.isEmpty()) {
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            queue.add(obj);
            o = monitors.removeFirst();
        }

        synchronized(o) {
            o.notify();
        }
    }

    public E get() {
        Object o = new Object();
        synchronized(lock) {
            monitors.add(o);
            lock.notify();
        }
        synchronized(o) {
            while (queue.isEmpty()) {
                try {
                    o.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return queue.removeFirst();
        }
    }
}

Автор: Prol 26.1.2008, 20:51
Цитата(LSD @ 23.1.2008,  15:41)
В любом случае проблема состоит в том, что надо усыплять поток до тех пор пока не придёт ответ. 

Усыплять поток - это всё равно что хальтовать процессор в ожидании немаскируемого прерывания :о))) Моветон :о) 
Да посади проц в фарева цикл на обработку задач :о)

Автор: ivg 26.1.2008, 21:54
Цитата(Prol @  26.1.2008,  20:51 Найти цитируемый пост)
Усыплять поток - это всё равно что хальтовать процессор в ожидании немаскируемого прерывания :о))) Моветон :о) 
Да посади проц в фарева цикл на обработку задач :о)

Мыслить в контексте однозадачности - вот моветон.  smile 
Вобщем, rtfm, как говорится.

Автор: Platon 26.1.2008, 22:27
Prol, от куда такие сведения? лупить в бесконечном цикле обработку задачи, которые поступают через неопределенные промежутки времени. Это убийство процессорного времени воистину!

Автор: Prol 26.1.2008, 23:24
Цитата(Platon @ 26.1.2008,  22:27)
Prol, от куда такие сведения? лупить в бесконечном цикле обработку задачи, которые поступают через неопределенные промежутки времени. Это убийство процессорного времени воистину!

Процессор должен работать, а человек - думать :о)

Не слипайте потоки, дайте им работу :о)
Если вы слипаете поток, значит он не нужен в вашей проге :о)
Зачем заводить поток, который вы же сами и останавливаете?

Вот классно сделано в транспьютерах: есть очередь процессоров, есть очередь задач,  свободный процессор отдаётся ожидающей задаче :о) Если процессорам нечего делать, значит все задачи выполнены :о)

Автор: LSD 27.1.2008, 04:16
Цитата(Prol @  26.1.2008,  20:51 Найти цитируемый пост)
Усыплять поток - это всё равно что хальтовать процессор в ожидании немаскируемого прерывания :о))) Моветон :о) 
Да посади проц в фарева цикл на обработку задач :о) 

Полная чушь!
1. Приостанавливать поток, и выполнять halt у процессора - это две большие разницы.
2. Если задача предполагает ожидание некого события, то поток должен заснуть, а не молотить никому не нужный цикл напрасно загружая процессор.

Добавлено через 46 секунд
Цитата(Prol @  26.1.2008,  23:24 Найти цитируемый пост)
Не слипайте потоки, дайте им работу :о)
Если вы слипаете поток, значит он не нужен в вашей проге :о)
Зачем заводить поток, который вы же сами и останавливаете?

Ты не понимаешь разницы между остановкой и приостановкой?

Автор: Prol 27.1.2008, 11:30
Цитата(LSD @ 27.1.2008,  04:16)
2. Если задача предполагает ожидание некого события, то поток должен заснуть

А какое отношение поток имеет к задаче? Это два разных объекта :о) Если задаче нужно чего-то ожидать, то причём тут поток? Поток передаётся другой задаче :о)

Автор: LSD 27.1.2008, 13:39
Цитата(Prol @  27.1.2008,  11:30 Найти цитируемый пост)
А какое отношение поток имеет к задаче? Это два разных объекта :о) Если задаче нужно чего-то ожидать, то причём тут поток? Поток передаётся другой задаче :о) 

Тяжкое наследие DOS-а, сами распределяем память, сами организуем многозадачность smile 

Во первых попробуй реализовать такой механизм, и посмотрим что у тебя получится smile 
Во вторых, что ты будешь делать если нужные данные поступили и пора возвращать управление первой задаче, а вторая задача которая заняла поток, еще и не думает заверщаться, или хуже того блокирована на IO-операции?

Автор: Prol 27.1.2008, 16:48
Цитата(LSD @ 27.1.2008,  13:39)
Цитата(Prol @  27.1.2008,  11:30 Найти цитируемый пост)
А какое отношение поток имеет к задаче? Это два разных объекта :о) Если задаче нужно чего-то ожидать, то причём тут поток? Поток передаётся другой задаче :о) 


Во первых попробуй реализовать такой механизм, и посмотрим что у тебя получится smile 
Во вторых, что ты будешь делать если нужные данные поступили и пора возвращать управление первой задаче, а вторая задача которая заняла поток, еще и не думает заверщаться, или хуже того блокирована на IO-операции?

Нет необходимости писать свое

Даг Ли создал отличную открытую библиотеку утилит параллельности, util.concurrent, которая включает объекты-мьютексы, семафоры, коллекции, такие как очереди и хэш-таблицы, хорошо работающие при параллельном доступе, и несколько реализаций рабочей очереди. Класс PooledExecutor из этого пакета - эффективная, широко использующаяся, правильная реализация пула потоков, основанного на рабочей очереди. Прежде чем пытаться писать собственное программное обеспечение, которое вполне может оказаться неправильным, вы можете рассмотреть использование некоторых утилит в util.concurrent. Библиотека util.concurrent включена в Java Development Kit 1.5.

Вот хороший учебник для овладения с практическими примерами.
http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html



Автор: Platon 27.1.2008, 17:23
В этом вопросе я уже не компетентен...

Добавлено через 9 минут и 33 секунды
хммм, в тамом случае, хотелось бы увидеть от Prol решения моей проблемы с помощью стандартного пакета Java, из этого я думаю, сможет получиться хорошая статья.

Автор: LSD 28.1.2008, 13:58
Цитата(Prol @  27.1.2008,  16:48 Найти цитируемый пост)
Даг Ли создал отличную открытую библиотеку утилит параллельности, util.concurrent, которая включает объекты-мьютексы, семафоры, коллекции, такие как очереди и хэш-таблицы, хорошо работающие при параллельном доступе, и несколько реализаций рабочей очереди. Класс PooledExecutor из этого пакета - эффективная, широко использующаяся, правильная реализация пула потоков, основанного на рабочей очереди. Прежде чем пытаться писать собственное программное обеспечение, которое вполне может оказаться неправильным, вы можете рассмотреть использование некоторых утилит в util.concurrent.

Из java.util.concurrent в данном случае, я вижу применимость только для Lock/Condition. Каким образом ты предлагаешь использовать PooledExecutor для данной задачи?

Автор: COVD 28.1.2008, 19:29
Не морочьте голову  smile .  В java есть только потоки (threads). В приложении может быть запущено много потоков. Потокам выделяется память и периодически порция процессорного времени в зависимости от приоритета потока. Если для выполнения действий в потоке требуется дождаться некоторого события, то поток переводится в состояние ожидания и свою порцию процессорного времени использовать не будет.
Prol, java потоки - это воплощение вашей идеи с независимыми приложениями, использующими общую память. Можете гордиться  smile .  

Автор: Prol 28.1.2008, 23:27
Цитата(COVD @ 28.1.2008,  19:29)
Prol, java потоки - это воплощение вашей идеи с независимыми приложениями, использующими общую память. 

К сожалению все они рухнут, если произойдёт operation exeption, access violation или overflow в самой JVM.

Автор: COVD 28.1.2008, 23:32
Ничто не вечно. 

Автор: Platon 19.2.2008, 19:06
Dear Lord, help me!!!

Я до сих пор не могу найти решения. Мое последнее решение также впадает в ступор:

Код

package platon.network;

import java.util.LinkedList;

public class ResponseWaitor1<E> {
    private final LinkedList<Flag> monitors = new LinkedList<Flag>();
    private final LinkedList<E> queue = new LinkedList<E>();

    public void add(E obj) {
        Flag o;
        synchronized(monitors) {
            while (monitors.isEmpty()) {
                try {
                    System.out.println(Thread.currentThread().getId() + " Waiting monitors");
                    monitors.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            o = monitors.removeFirst();
        }
        System.out.println(Thread.currentThread().getId() + " Monitor passed");
        o.done();
        System.out.println(Thread.currentThread().getId() + " Flag got");
        synchronized(queue) {
            queue.add(obj);
            System.out.println(Thread.currentThread().getId() + " Queue notify");
            queue.notify();
        }
    }

    public E get() {
        Flag o = new Flag();
        synchronized(monitors) {
            monitors.add(o);
            System.out.println(Thread.currentThread().getId() + " Monitor notify");
            monitors.notify();
        }

        while (!o.isDone()) {
            synchronized(o) {
                try {
                    System.out.println(Thread.currentThread().getId() + " Waiting flag");
                    o.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        System.out.println(Thread.currentThread().getId() + " Flag passed");

        synchronized(queue) {
            while (queue.isEmpty()) {
                try {
                    System.out.println(Thread.currentThread().getId() + " Waiting queue");
                    queue.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getId() + " queue passed");
            return queue.removeFirst();
        }
    }

    public static void main(String[] args) {
        final ResponseWaitor1<Integer> waitor = new ResponseWaitor1<Integer>();



        new Thread(new Runnable() {
            int i;
            public void run() {
                for (int i = 0; i < 2000; i++) {
                    System.out.println(Thread.currentThread().getId() + " " + waitor.get());
                }
            }
        }).start();

        new Thread(new Runnable() {
            int i;
            public void run() {
                for (int i = 0; i < 2000; i++) {
                    System.out.println(Thread.currentThread().getId() + " " + waitor.get());
                }
            }
        }).start();

        new Thread(new Runnable() {
            int i;
            public void run() {
                for (int i = 0; i < 4000; i++) {
                    waitor.add(i);
                }
            }
        }).start();
    }
}



Добавлено @ 19:11
Я не уверен как правильно называется класс Flag, возможно это некий семафор.

Автор: SoulKeeper 19.2.2008, 22:40
Я конечно понимаю что изобретение своего велосипеда делает программиста более квалифицированым, но есть

1). RMI. Правда кажись работает на блокирующем I/O, в дебри не влезаю, да и для моих потребностей, обслуживание от силы 15 клиентов, мне nio не особо и нужно.
2). JERI в Jini. В свое время обращал внимание, но в глубь копать не стал, т.к. вариант #1 подошел вполне. Кажись есть возможность использование nio.

Собственно вот такие вот дела smile

Автор: Platon 20.2.2008, 10:32
Зачем мне ваши технологии, когда я могу без них обойтись. Я с удовольствием перейму стандартное решение текущей задачи, но технологии мне советовать не надо.

Автор: SoulKeeper 20.2.2008, 11:03
Вообще-то это и есть стандартные решения текущей задачи.

Автор: Platon 20.2.2008, 11:26
задача сводится лишь к тому чтобы из асинхронной работы была возможность запросить и дождаться ответа от общего объекта, причем в пределах одной JVM. Спрашивается зачем мне RMI???

Автор: Platon 20.2.2008, 13:16
Последнее из выведенного на экран оказывается всегда похожим на

Код

11 Monitor passed
11 Flag got
11 Queue notify
11 Waiting monitors
10 Waiting flag


Всегда заканчивается ожиданием готовности флажка. и почему то в этот момент он не срабатывает :( хотя во 2-й строчке он уже был выброшен.

Автор: Platon 22.2.2008, 13:20
Вот оно!!! Как говорится: "Эврика"!!!

Код

public class ResponseWaitor1<E> {
    private final LinkedList<Flag> monitors = new LinkedList<Flag>();
    private final LinkedList<E> queue = new LinkedList<E>();

    public void add(E obj) {
        Flag o;
        synchronized(monitors) {
            while (monitors.isEmpty()) {
                try {
                    //System.out.println(Thread.currentThread().getId() + " Waiting monitors");
                    monitors.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            o = monitors.removeFirst();
        }
        //System.out.println(Thread.currentThread().getId() + " Monitor passed");
        o.done();
        //System.out.println(Thread.currentThread().getId() + " Flag got");
        synchronized(queue) {
            queue.add(obj);
            //System.out.println(Thread.currentThread().getId() + " Queue notify");
            queue.notify();
        }
    }

    public E get() {
        Flag o = new Flag();
        synchronized(monitors) {
            monitors.add(o);
            //System.out.println(Thread.currentThread().getId() + " Monitor notify");
            monitors.notify();
        }

        synchronized(o) {
            while (!o.isDone()) {
                try {
                    //System.out.println(Thread.currentThread().getId() + " Waiting flag" + o.isDone());
                    o.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        //System.out.println(Thread.currentThread().getId() + " Flag passed");

        synchronized(queue) {
            while (queue.isEmpty()) {
                try {
                    //System.out.println(Thread.currentThread().getId() + " Waiting queue");
                    queue.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //System.out.println(Thread.currentThread().getId() + " queue passed");
            return queue.removeFirst();
        }
    }

    public static void main(String[] args) {
        final ResponseWaitor1<Integer> waitor = new ResponseWaitor1<Integer>();

        new Thread(new Runnable() {
            int i;
            public void run() {
                for (int i = 0; i < 40000; i++) {
                    waitor.add(i);
                }
            }
        }).start();

        new Thread(new Runnable() {
            int i;
            public void run() {
                for (int i = 0; i < 20000; i++) {
                    System.out.println(Thread.currentThread().getId() + " " + waitor.get());
                }
            }
        }).start();

        new Thread(new Runnable() {
            int i;
            public void run() {
                for (int i = 0; i < 20000; i++) {
                    System.out.println(Thread.currentThread().getId() + " " + waitor.get());
                }
            }
        }).start();
    }
}


Жаль, что мне никто не помог, и столько времени пришлось ломать голову над этим пустячком.
Проблема оказалась вот в этом куске:

Код

 while (!o.isDone()) {
            synchronized(o) {
                try {
                    System.out.println(Thread.currentThread().getId() + " Waiting flag");
                    o.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }


Поменял на 

Код

synchronized(o) {
            while (!o.isDone()) {
                try {
                    //System.out.println(Thread.currentThread().getId() + " Waiting flag" + o.isDone());
                    o.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }


Добавлено через 10 минут
Я бы сам себе за такие мучения орден вручил бы!

Автор: COVD 22.2.2008, 16:42
Очень удобен для таких задач класс java.util.concurrent.LinkedBlockingQueue - никаких wait и synchronized не нужно. Единственное неудобство - эта очередь не принимает на входе null. Поэтому конец очереди , если есть в нем необходимость, можно определить только поставив в очередь специальный обьект и ловить его на выходе через instanceof  .

Автор: Platon 22.2.2008, 16:47
COVD, дорогой, где ж вы неделю назад были???!!!

Автор: COVD 22.2.2008, 17:40
Я просто не сразу понял, с чем вы сражались.  smile 

Еще мне кажется, вы не предусмотрели таймаут на ожидание ответа. Ответ ведь может никогда и не придти. А в LinkedBlockingQueue  его можно поставить и тогда, если время ожидания ответа превысит таймаут, очередь выбросит null (поэтому null и запрещен на входе).

Автор: Platon 22.2.2008, 18:13
Смотрим сюда.
Код

public class ResponseWaitor1<E> {
    private LinkedBlockingQueue<E> queue = new LinkedBlockingQueue<E>();

    public void add(E obj) {
        queue.add(obj);
    }

    public E get() {
        return queue.poll();
    }

    public static void main(String[] args) {
        final ResponseWaitor1<Integer> waitor = new ResponseWaitor1<Integer>();

        new Thread(new Runnable() {
            int i;
            public void run() {
                for (int i = 0; i < 40000; i++) {
                    waitor.add(i);
                }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {

                }
                System.out.println("There are " + waitor.queue.size() + " items left in queue");
            }
        }).start();

        new Thread(new Runnable() {
            int i;
            public void run() {
                for (int i = 0; i < 20000; i++) {
                    System.out.println(Thread.currentThread().getId() + " " + waitor.get());
                }
            }
        }).start();

        new Thread(new Runnable() {
            int i;
            public void run() {
                for (int i = 0; i < 20000; i++) {
                    System.out.println(Thread.currentThread().getId() + " " + waitor.get());
                }
            }
        }).start();

        
    }
}


Теперь смотрим на конец вывода в консоль:

Цитата

10 39988
10 39989
10 39990
10 39991
10 39992
There are 7 items left in queue

проблемка возникла... судя по всему, в некоторые некоторые моменты poll() выдают одинаковый результат 2-м потокам.

Автор: COVD 22.2.2008, 18:34
у меня ваш код нормально сработал - последнее выведенное число 39999. А надпись "There are .." выскакивает где-то в середине вывода на консоль, т.е. это еще не конец работы программы.

Автор: Platon 22.2.2008, 18:41
не-не, надо несколько раз прогу запустить + если где-то в середине, увеличьте задержку до 10 секунд.

Автор: COVD 22.2.2008, 19:08
зачем вообще эта задержка? поток, который кладет данные в очередь, может закончить свою работу в любое время. независимо от читающих

Автор: Platon 22.2.2008, 19:13
Ок, код некомпетентный, вот новый:

Код

public class ResponseWaitor1<E> {
    private LinkedBlockingQueue<E> queue = new LinkedBlockingQueue<E>();

    public void add(E obj) {
        queue.add(obj);
    }

    public E get() {
        return queue.poll();
    }

    public static void main(String[] args) throws InterruptedException {
        final ResponseWaitor1<Integer> waitor = new ResponseWaitor1<Integer>();

        Thread t1 = new Thread(new Runnable() {
            int i;
            public void run() {
                for (int i = 0; i < 40000; i++) {
                    waitor.add(i);
                }
            }
        });
        t1.start();

        Thread t2 = new Thread(new Runnable() {
            int i;
            public void run() {
                for (int i = 0; i < 20000; i++) {
                    System.out.println(Thread.currentThread().getId() + " " + waitor.get());
                }
            }
        });
        t2.start();

        Thread t3 = new Thread(new Runnable() {
            int i;
            public void run() {
                for (int i = 0; i < 20000; i++) {
                    System.out.println(Thread.currentThread().getId() + " " + waitor.get());
                }
            }
        });
        t3.start();
        t1.join();
        t2.join();
        t3.join();
        System.out.println("There are " + waitor.queue.size() + " items left in queue");
    }
}


Добавлено через 2 минуты и 50 секунд
Если что, сразу скажу, пробовал add/get делать синхронизированными.

Автор: Platon 25.2.2008, 13:54
Вот и подошла к концу моя эпопея. Спасибо COVD за отличную подсказку, работа моего примера была неправильной из-за метода poll() который не ждет появления объекта в очереди, а сразу выбрасывает null.
Правильным решением здесь будет take() Он точно ждет.

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