views:

801

answers:

4

Certain methods in Java will block until they can do something, like ServerSocket.accept() and InputStream.read(), but how it does this is not easy for me to find. The closest thing I can think of is a while() loop with a Thread.sleep() each time through, but the longer the sleep period, the less responsive the blocking, and the shorter the sleep, the more spinning that occurs.

So, I guess I have one or two questions: 1. how do various standard functions (like the ones above) block? Native code? while() loops? Something else? and 2. How should I implement methods that block?

+7  A: 

The operations you've listed block because of the underlying platform (ie. native code).

You can implement a block using Java's Object.wait() and Object.notify() methods; wait() will block the calling thread until another thread calls notify() on the same lock.

MandyK
+2  A: 

I can answer part 1. They use the OS to handle it since each OS already has signalling and blocking built in as part of enabling multi-tasking.

Usually, if you're having to implement your own spinning lock code then either you're using the wrong methods. Spinning also reduces any form of power management even with nice sleeps the code still has to wake up to do nothing.

Paul Hargreaves
+4  A: 

To answer (2) you want the Object methods wait, notify and notifyAll.

When using Object.wait(), you must enclose the wait in a loop which checks the condition on which you are waiting. The wait() method can return spuriously (for subtle reasons).

Also, you need to give consideration to whether you need to use notify() or notifyAll() - if all of the waiting threads are waiting for exactly the same condition and you only need one to handle whatever it is you are notifying for, you can use notify(), otherwise you need to use notifyAll(), since you have no guarantees which thread will be notified on notify() - you cannot expect, for example, that they are round-robined.

Finally, make sure you take a look at the java.util.concurrent package - it has many higher level and better solutions to common threading concerns than using the Java primitives.

Software Monkey
+3  A: 

The essential answer to (1) is "don't worry about it -- the OS handles it". Calls to things like reading from input streams are essentially wrappers around operating system calls. Under the hood inside the OS, what I think is usually happening when a call "blocks" in these cases is that the OS "knows" that it is waiting for a hardware interrupt from, say, the disk controller to say that such-and-such requested data is now available, and it knows that Thread X was the one that requested that data. So it doesn't schedule in Thread X again until it receives that interrupt (or an interrupt saying "there was an error" etc). (And part of the thread scheduling algorithm is then doing things like giving the waiting thread a temporary "boost" of some kind when that waited-for data becomes available. Again, usually you don't need to worry too much about this.) Or put another way: whatever the exact details of this mechanism, it's not available to the general Java programmer.

In (2), I would suggest thinking more about "how do I do Thing X, which might happen to block". I think the answer is hardly ever that Thing You Want To Do is deliberately just "block", and whatever Thing X is, there's probably a library method/class that will do it for you. For example (links include some material I've written on these subjects):

  • if you want to take the next message/job when it becomes available from some queue/provider, look at blocking queues
  • if you need to control access to a shared resource with a "lock" on an object, waiting for the lock to become available if necessary, consider plain old synchronized, or an explicit lock;
  • if you want to wait for one of many pooled resources to become available, look at semaphores

I'd say that the raw wait/notify mechanism is largely deprecated with the Java 5 concurrency API. And whatever you're doing, spinlocking is usually the very very last resort.

Neil Coffey