views:

148

answers:

6

so i want to have an arraylist that stores a series of stock quotes. but i keep track of bid price, ask price and last price for each.

of course at any time, the bid ask or last of a given stock can change.

i have one thread that updates the prices and one that reads them.

i want to make sure that when reading no other thread is updating a price. so i looked at synchronized collection. but that seems to only prevent reading while another thread is adding or deleting an entry to the arraylist.

so now i'm onto the wrapper approach:

public class Qte_List {
private final ArrayList<Qte> the_list;

public void UpdateBid(String p_sym, double p_bid){
    synchronized (the_list){
        Qte q = Qte.FindBySym(the_list, p_sym);
        q.bid=p_bid;}
}

public double ReadBid(String p_sym){
    synchronized (the_list){
        Qte q = Qte.FindBySym(the_list, p_sym);
        return q.bid;}
}

so what i want to accomplish with this is only one thread can be doing anything - reading or updating an the_list's contents - at one time. am i approach this right?

thanks.

+1  A: 

Yes this will work, anyway you don't need to do it yourself since it is already implemented in the Collections framework

Collections.synchronizedList

stacker
A synchronized List won't work, since he updates attributes of the entries at the list in that syncronized block
Hardcoded
+1  A: 

That looks like a reasonable approach. Nit-picking, though, you probably shouldn't include the return statement inside the synchronized block:

public double ReadBid(String p_sym){
    double bid;
    synchronized (the_list) {
        Qte q = Qte.FindBySym(the_list, p_sym);
        bid = q.bid;
    }

    return bid;
}

I'm not sure if it's just my taste or there's some concurrency gotcha involved, but at the very least it looks cleaner to me ;-).

Santa
+2  A: 

Yes, you are on the right track and that should work.

But why not use the existing Hashtable collection, which is synchronized, and provides a key-value lookup already?

Tim Drisdelle
Hashtable (and Vector) are pre-JDK 1.2 and should be avoided. Instead, consider Collections.synchronizedMap(Map m) to create a thread-safe HashMap. Also, although the OP hasn't explicitly stated it I suspect that the quotes need to be ordered as per a typical order-book.
Adamski
+1  A: 

Your approach should do the trick, but as you stated, there can only be one reader and writer at a time. This isn't very scaleable.

There are some ways to improve performance without loosing thread-safety here.
You could use a ReadWriteLock for example. This will allow multiple readers at a time, but when someone gets the write-lock, all others must wait for him to finish.

Another way would be to use a proper collection. It seems you could exchange your list with a thread-safe implementation of Map. Have a look at the ConcurrentMap documentation for possible candidates.

Edit:
Assuming that you need ordering for your Map, have a look at the ConcurrentNavigableMap interface.

Hardcoded
+1  A: 

What you have will work, but locking the entire list every time you want to read or update the value of an element is not scalable. If this doesn't matter, then you're fine with what you have. If you want to make it more scalable consider the following...

You didn't say whether you need to be able to make structural changes to the_list (adding or removing elements), but if you don't, one big improvement would be to move the call to FindBySym() outside of the synchronized block. Then instead of synchronizing on the_list, you can just synchronize on q (the Qte object). That way you can update different Qte objects concurrently. Also, if you can make the Qte objects immutable as well you actually don't need any synchronization at all. (to update, just use the_list[i] = new Qte(...) ).

If you do need to be able to make structural changes to the list, you can use a ReentrantReadWriteLock to allow for concurrent reads and exclusive writes.

I'm also curious why you want to use an ArrayList rather than a synchronized HashMap.

Angus
does seem a hashmap makes much more sense. i probably default to list too much without thinking.the immutable thing on a single Qte object might help part of the problem. but i need to work a little more to understand it. what is the syntax that makes Qte objects immutable, or i will search. also, does immutable mean that to 'change' the object i need to re-assign it? (i'm looking at the text in the response saying, "to update, just use the_list[i] = new Qte(...)"
jeff
Immutable means "cannot be changed". For example Strings are immutable, whereas StringBuffers are mutable. You can't change the value of a String object (e.g., you can't say String s = "a"; s.append("b"); . An immutable Qte class would have bid price, ask price and last price as final member variables and these 3 values would be passed into the constructor. The member variables can be public or you can have a getter (but no setter) for each.
Angus
Also, regarding DJClayworth's comment that "you will either get the price before or the price after the write", this is only true if Qte.bid is a 32-bit (or less) value or volatile. For 64-bit non-volatile values it's possible to read in the middle of a write and get bad data (see the java language spec, section 17.7 for details) so you need to either change Qte.bid to be a float or int or leave it a double and declare it volatile (if it's not already). Once that's done you you can safely read and write q.bid without synchronization. (and you can ignore my immutability suggestion)
Angus
A: 

As I understand it you are using the map to store the quotes; the number of quotes never changes, but each quote can be read or modified to reflect current prices. It is important to know that locking the collection only protects against changes to which Quote objects are in the map: it does not in any way restrict the modification of the contents of those Quotes. If you want to restrict that access you will have to provide locking on the Quote object.

Looking at your code however I don't believe you have a significant synchronization problem. If you try to do a read at the same time as a write, you will either get the price before or the price after the write. If you didn't know the write was going to occur that shouldn't matter to you. You may need locking at a higher level so that

if (getBidPrice(mystock)<10.0) {
  sell(10000);
}

happens as an atomic operation and you don't end up selling at 5.0 rather than 10.0.

If the number of quotes really doesn't change then I would recommend allowing Qte objects to be added only in the constructor of Qte_List. This would make locking the collection irrelevant. The technical term for this is making Qte_List immutable.

DJClayworth
thanks you make me think i need to clarify more precisely what to lock. will noodle on it a few hours.
jeff
DJ - also, i think i've gotten myself confused now in trying to think through this. i'm looking at what you wrote in bold above .... what is the difference between locking on a collection and marking it final? i thought marking it final was what prevented changes to which objects are in the map.
jeff
@Jeff final only declares the variable as "immutable", not the object at the variable. So the List is still mutable. If you want the List to be immutable, see `Collections.unmodifiableList()`
Hardcoded
If your list of quotes doesn't change then I would recommend allowing Qte objects to be added only in the constructor of Qte_List. Then locking the collection becomes irrelevant, and so does making Qte_List final. The technical term for this is making Qte_List 'immutable'.
DJClayworth