J2ME/Java: обращение к StringBuffer через потоки

Этот вопрос может быть длинным, но я хочу предоставить много информации.

Обзор. Я создаю приложение Stock Quotes Ticker для Blackberry. Но у меня проблемы с моим StringBuffer, который содержит информацию об отдельных акциях.

Процесс: мое приложение подключается к нашему серверу через SocketConnection. Сервер отправляет отформатированный набор строк, содержащий последнюю сделку по акциям. Таким образом, всякий раз, когда происходит новая сделка, сервер будет отправлять отдельные котировки акций этой сделки. Через InputStream я могу прочитать эту информацию и поместить каждый символ в StringBuffer, на который ссылаются потоки. Анализируя на основе char3, я могу определить набор котировок/информации об акциях.

char1 - для разделения данных char3 - означает конец биржевой котировки/информации

пример формата котировок акций, отправленный нашим сервером: stock_quote_name(char 1)some_data(char1)some_data(char1)(char3)

Затем мое приложение анализирует эту котировку акций, чтобы сравнить определенные данные, и форматирует их так, как они будут выглядеть при отображении на экране. Когда сделки происходят постепенно (медленно), приложение работает отлично. Тем не мение..

Проблема: когда сделки происходят слишком быстро и почти одновременно, приложение "Мое" не может эффективно обрабатывать отправленную информацию. Содержимое StringBuffer объединяется со следующей сделкой. Значение Две биржевые данные в одном StringBuffer.

поле должно быть следующим: Stock_quote_name some_data some_data

пример происходящего: Stock_quote_name some_data some_dataStock_quote_name some_data some_data

вот мой код для этой части:

while (-1 != (data = is.read()))
                {
                       sb.append((char)data);
                       while(3 != (data = is.read()))
                       {
                           sb.append((char)data);
                       }
                       UiApplication.getUiApplication().invokeLater(new Runnable()
                       {
                           public void run()
                           {
                               try
                               {
                                   synchronized(UiApplication.getEventLock())
                                   {
                                       SetStringBuffer(sb);
                                       DisplayStringBuffer();
                                       RefreshStringBuffer();
                                   }
                               } catch (Exception e)
                               {
                                   System.out.println("Error in setting stringbuffer: " + e.toString());
                               }
                           }
                       });
                }

public synchronized void DisplayStringBuffer()
{
    try
    {
        //parse sb - string buffer
        ......
    }
    catch(Exception ex)
    {
        System.out.println("error in DisplayStringBuffer(): " + ex.toString());
    }
}
public synchronized void SetStringBuffer(StringBuffer dataBuffer)
{
    this.sb =dataBuffer;
    System.out.println(sb);
}
public synchronized void RefreshStringBuffer()
{
    this.sb.delete(0, this.sb.length());
}

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

Мой вопрос: есть ли у вас какие-либо предложения о том, как я могу поместить данные в StringBuffer без добавления следующей информации к первому содержимому?


person Jemuel Dalino    schedule 01.03.2010    source источник


Ответы (3)


Часть, где вы читаете данные, синхронизируется, а часть, где вы добавляете данные в буфер, — нет. Если вы каждый раз повторно используете один и тот же StringBuffer, у вас возникнет состояние гонки.

person Gabe    schedule 01.03.2010
comment
Итак, вы предлагаете мне создавать новый StringBuffer каждый раз, когда повторяется цикл While? - person Jemuel Dalino; 01.03.2010

Что ж, поскольку вы используете invokeLater для установки/отображения/очистки вашего StringBuffer, вы правы, ничто не мешает вам вернуться к следующему вызову read() и изменить этот StringBuffer до того, как вы сможете его отобразить.

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

Если вы хотите использовать invokeLater, вам нужно убедиться, что вы не добавляете к своему StringBuffer поток чтения входного потока, пока ваш поток событий пытается отобразить данные в пользовательском интерфейсе. Таким образом, вы, вероятно, рассмотрите возможность создания нового StringBuffer для каждого Runnable.

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

while (-1 != (data = is.read()))
            {
                   sb.append((char)data);
                   while(3 != (data = is.read()))
                   {
                       sb.append((char)data);
                   }

                   synchronized(UiApplication.getEventLock())
                   {
                       SetStringBuffer(sb);
                       DisplayStringBuffer();
                       RefreshStringBuffer();
                   } 
            }
person Bradley    schedule 10.05.2010

Вы не можете повторно использовать один и тот же StringBuffer для каждой котировки акций, потому что вы читаете следующую котировку акций до того, как поток пользовательского интерфейса завершит отображение последней. Помните, что вы пишете в StringBuffer в одном потоке и читаете StringBuffer в другом. Нет никакой гарантии, что поток пользовательского интерфейса (поток чтения) вызвал RefreshStringBuffer() до того, как цикл повторится и начнет добавлять следующую котировку акций в StringBuffer.

Вместо этого используйте коллекцию строк.

java.util.queue<String> q = new java.util.concurrent.ConcurrentLinkedQueue<String>();

затем сделайте q.add(sb.toString());, когда закончите помещать цитату в sb.

для отображения котировок

public void DisplayStockQuote() {
    while(!q.isEmpty()) {
        String s = q.poll();

        // display s
        try
        {
            //parse s - string containing stock quote
            ......
        }
        catch(Exception ex)
        {
            System.out.println("error in DisplayStringBuffer(): " + ex.toString());
        }

    }
}

Вызовите этот метод из потока пользовательского интерфейса вместо

SetStringBuffer(sb);
DisplayStringBuffer();
RefreshStringBuffer();

Я предлагаю вам попробовать избежать дополнительной темы и использовать решение Брэдли. Но если вы действительно хотите иметь два потока, что-то вроде этого будет работать.

person Jay    schedule 10.05.2010