J2ME / Java: ссылка на StringBuffer через потоки - PullRequest
0 голосов
/ 01 марта 2010

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

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

Процесс : мое приложение подключается к нашему серверу через SocketConnection. Сервер отправляет отформатированный набор строк, который содержит последнюю биржевую сделку. Таким образом, всякий раз, когда происходит новая сделка, сервер отправляет отдельную котировку акций этой сделки. Через InputStream я могу прочитать эту информацию и поместить каждый символ в StringBuffer, на который ссылается Threads. С помощью анализа на основе 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 без добавления следующей информации к первому контенту

Ответы [ 3 ]

0 голосов
/ 10 мая 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();
                   } 
            }
0 голосов
/ 10 мая 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();

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

0 голосов
/ 01 марта 2010

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

...