From: Alan Gutierrez on
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
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
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
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
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