views:

234

answers:

3

This question might be long, but I want to provide much information.

Overview: I'm creating a Stock Quotes Ticker app for Blackberry. But I'm having problems with my StringBuffer that contains an individual Stock information.

Process: My app connects to our server via SocketConnection. The server sends out a formatted set of strings that contains the latest Stock trade. So whenever a new trade happens, the server will send out an individual Stock Quote of that trade. Through an InputStream I am able to read that information and place each character in a StringBuffer that is referenced by Threads. By parsing based on char3 I am able to determine a set of stock quote/information.

char1 - to separate data char3 - means end of a stock quote/information

sample stock quote format sent out by our server: stock_quote_name(char 1)some_data(char1)some_data(char1)(char3)

My app then parses that stock quote to compare certain data and formats it how it will look like when displayed in the screen. When trades happen gradually(slow) the app works perfectly. However..

Problem: When trades happen too quickly and almost at the same time, My app is not able to handle the information sent efficiently. The StringBuffer has its contents combined with the next trade. Meaning Two stock information in one StringBuffer.

field should be: Stock_quote_name some_data some_data

sample of what's happening: Stock_quote_name some_data some_dataStock_quote_name some_data some_data

here's my code for this part:

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());
}

From what I can see, when trades happen very fast, The StringBuffer is not refreshed immediately and still has the contents of the previous trade, when i try to put new data.

My Question is: Do you guys have any suggestion on how i can put data into the StringBuffer, without the next information being appended to the first content

A: 

The part where you read data is synchronized, but the part where you append data to the buffer is not. If you are reusing the same StringBuffer each time, you will have a race condition.

Gabe
So are you suggesting I create a new StringBuffer each time the While loop iterates?
Jemuel Dalino
A: 

Well, since you are using invokeLater to set / display / clear your StringBuffer, you're right, there's nothing blocking you from going back up to the next read() call and modifying that StringBuffer before you're able to dispaly it.

Obviously you're not reading from the input stream from your event thread, so in order to update the UI you need to either use invokeLater OR synchronize on the event lock, but you're doing both.

If you want to use invokeLater, then you'll need to make sure you don't append to your StringBuffer with your input stream reader thread while your event thread is attempting to display the data in the UI. Thus, you'll probably look into creating a new StringBuffer for each Runnable.

If you want to synchronize on the event lock, something like this could work (which might work better as it avoids unnecessary garbage creation with creating new 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();
                   } 
            }
Bradley
A: 

You can't reuse the same StringBuffer for each stock quote because you are reading the next stock quote before the UI thread has finished displaying the last one. Remember, you are writing to the StringBuffer on one thread, and reading the StringBuffer on another. There is no guarantee that the UI thread (reading thread) has called RefreshStringBuffer() before the loop iterates and starts appending the next stock quote to the StringBuffer.

Use a collection of Strings instead.

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

then do q.add(sb.toString()); when your done putting the quote into sb.

to display the quotes

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());
        }

    }
}

Call that method from your UI thread instead of

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

I suggest you try avoiding the extra thread and go with Bradley's solution. But if you really want to have two threads, something like this will work.

Jay