Prev: Const correctness (was Re: Oppinion on 'least priviledge', 'const correctness', etc.)
Next: Simple Hack To Get $2500 To Your PayPal Account.
From: Alan Gutierrez on 20 Jul 2010 21:02 I'm implementing a log and I have a logging thread that writes log entires to file. Below is skeletal code to illustrate a question I have about concurrency and memory. public class Queued { public void main(String[] args) { final LinkedBlockingQueue<List<Integer>> queue = new LinkedBlockingQueue<List<Integer>>(); Thread thread = new Thread(new Runnable() { public void run() { for (;;) { try { List<Integer> list = queue.take(); if (list.get(0) != 0) { continue; } } catch (InterruptedException e) { continue; } } } }); thread.start(); List<Integer> list = new ArrayList<Integer>(); list.add(1); queue.offer(list); list = new ArrayList<Integer>(); list.add(0); queue.offer(list); thread.join(); } } As you can see, I'm using a normal list and passing it through a blocking queue to a thread that pulls a list form the queue and acts on the first element in the list. This code appears to me to be broken. The list is not synchronized or otherwise thread safe, so when the taker thread obtains a reference to the list, the list may be empty. It wouldn't be a question if I was using an immutable object, but is there a good way to ensure that mutable objects are flushed before sending them off to another thread? (Or does the above code work and I just don't know it?) -- Alan Gutierrez - alan(a)blogometer.com - http://twitter.com/bigeasy
From: markspace on 20 Jul 2010 21:20 Alan Gutierrez wrote: > This code appears to me to be broken. The list is not synchronized or > otherwise thread safe, so when the taker thread obtains a reference to > the list, the list may be empty. Actually, the LinkedBlockingQueue is synchronized. Why do you say it's not? It's direct parent, BlockingQueue, makes the memory consistency guarantee. On the other hand, this is a problem: > try { .... > } catch (InterruptedException e) { > continue; > } Interrupts do not happen spuriously or for no reason. If you get an interrupt, someone has requested that your thread exit. It's best to do so. I'd recommend something like: public void run() { try { for (;;) { List<Integer> list = queue.take(); if (list.get(0) != 0) { continue; } } } catch (InterruptedException e) { } } In other words, go ahead and catch the exception to prevent ugly messages, but "handle" it by exiting. This is almost always the correct response.
From: Joshua Cranmer on 20 Jul 2010 21:24 On 07/20/2010 09:02 PM, Alan Gutierrez wrote: > It wouldn't be a question if I was using an immutable object, but is > there a good way to ensure that mutable objects are flushed before > sending them off to another thread? (Or does the above code work and I > just don't know it?) java.util.concurrent.BlockingQueue says: Memory consistency effects: As with other concurrent collections, actions in a thread prior to placing an object into a BlockingQueue happen-before actions subsequent to the access or removal of that element from the BlockingQueue in another thread. -- Beware of bugs in the above code; I have only proved it correct, not tried it. -- Donald E. Knuth
From: Alan Gutierrez on 20 Jul 2010 22:06 Joshua Cranmer wrote: > On 07/20/2010 09:02 PM, Alan Gutierrez wrote: >> It wouldn't be a question if I was using an immutable object, but is >> there a good way to ensure that mutable objects are flushed before >> sending them off to another thread? (Or does the above code work and I >> just don't know it?) > > java.util.concurrent.BlockingQueue says: > Memory consistency effects: As with other concurrent collections, > actions in a thread prior to placing an object into a BlockingQueue > happen-before actions subsequent to the access or removal of that > element from the BlockingQueue in another thread. Thank you. That answers my question. I'm much relieved. Something I'd read in the recent immutability thread had me thinking that I had everything all wrong. It was all the talk about the importance of immutability that made me worry that field assignments that were unsynchronized or non-volatile could be hidden from the receiving thread. This bit of documentation that I missed, plus a first reading of JLS Ch 17, put that to rest. -- Alan Gutierrez - alan(a)blogometer.com - http://twitter.com/bigeasy
From: Alan Gutierrez on 20 Jul 2010 22:19
markspace wrote: > Alan Gutierrez wrote: > >> This code appears to me to be broken. The list is not synchronized or >> otherwise thread safe, so when the taker thread obtains a reference to >> the list, the list may be empty. > > Actually, the LinkedBlockingQueue is synchronized. Why do you say it's > not? > > It's direct parent, BlockingQueue, makes the memory consistency guarantee. Joshua Cramer's snipped bit of the API docs spelled this out for me a little more clearly. I probably would have frustrated you by accusing you of misunderstanding my question had I not seen that first. > On the other hand, this is a problem: > > > try { > ... > > } catch (InterruptedException e) { > > continue; > > } > > Interrupts do not happen spuriously or for no reason. If you get an > interrupt, someone has requested that your thread exit. InterruptedException is a curious case for me. It is my understanding that the only someone who will request that my thread exit would be me. Not in this example, but in the log that I am implementing, the logging thread is an implementation detail. I have provided a way to terminate the log by sending a terminal value, and this is not exposed to the client, either. Its what happens when call shutdown on a facade, the client does not add items to the queue directly, either, so they can't log the terminal value. With a terminal value to shutdown the queue, and with the thread unexposed to the client, there seems to be no way to send an interrupt to the thread, so it becomes the classic unreachable checked exception block. How do you handle the interrupt that never happens? I decided that trying again after the impossible was as good as giving into the impossible. Unless there is some mechanism in Java that sends interrupts to threads at shutdown of which I'm not aware. Honestly, I'm not sure how Java stops say, a Thread.setDaemon() thread, but I always assumed it was synonymous to kill -9. -- Alan Gutierrez - alan(a)blogometer.com - http://twitter.com/bigeasy |