Java author mentions: Threads can simplify the development of complex systems by turning complicated asynchronous code into simpler straight-line code.

Again the book says: Threads make it easier to model how humans work and interact, by turning asynchronous workflows into mostly sequential ones. They can also turn otherwise convoluted code into straight line code that is easier to write, read, and maintain.

Am unclear with above two points.

My understanding is,

1) Multi-threading can be used for either:

  • Mutually exclusive tasks, CPU bound. This is parallel programming.

  • Concurrent tasks, CPU bound. Involves synchronization mechanism.

2) Asynchronous code(with Event loop) is relevant for mutually exclusive tasks that are IO bound. This is single threaded code.

Edit after reading comments:

Async programming is like, Chess champion, Polgar(is CPU) playing with multiple amateur chess players(are tasks). Nature of this game is amateur(task) need some time before Polgar(CPU) can come back with next move.

How is Async implemented?

  • Task capability – Suspend & Resume
  • Event loop scheduling these tasks

There is no more than one thread involved.

Non-blocking IO basically involves select() UNIX system call, under the hood. For example, python code below,

def runForever(self):
        while True:
            readers, _, _ = select.select(self.readers, [], [])
            for reader in readers:

Non-blocking IO is completely different programming model compared to Async IO. But,

Can a programming model unify all three, non-blocking, asynchronous nature & multithread support? Yes. Chrome V8 engine is an example, shown in this diagram.

How multi-threading help problems solved by asynchronous code, to provide straight line coding?

  • Seems like your question is already answered on Stack Overflow: What is the difference between asynchronous programming and multithreading? – rwong Oct 7 '17 at 13:09
  • A new thread is the way you asynchronously process a task that is CPU-bound. The word asynchronous describes a behavior, not a technique; you can make code asynchronous by spinning up another thread, or (in the case of I/O bound tasks), using continuations or callbacks. – Robert Harvey Oct 7 '17 at 14:41
  • @DerekElkins Do you think javax.swing.SwingUtilities help us write straight line code, with SwingUtilities.invokeLater() facility that provides corresponding EDT ? – user1787812 Oct 7 '17 at 21:26
  • Seems you put that comment in the wrong place. – Robert Harvey Oct 7 '17 at 21:27
  • I'm afraid your edit is (mostly) wrong. To reiterate, asynchronous programming is not a technique; it is a behavior that can be implemented using various techniques. There is at least one thread involved; sometimes more than one. Non-blocking I/O and Async I/O are essentially the same thing. – Robert Harvey Oct 7 '17 at 21:30

Would you rather write:

void foo(Channel<Channel<int>> chan1) {
    chan1.read((Channel<int> chan2) -> {
        chan2.read((int result) -> {


void foo(Channel<Channel<int>> chan1) {
    Channel<int> chan2 = chan1.read();
    int result = chan2.read();

or even:

void foo(Channel<Channel<int>> chan1) {


Let alone the hell that the first example would be in Java pre-lambdas. Things like futures and promises can make the first example look a little nicer, but won't eliminate the fundamental loss of stack discipline. This impacts the scoping of variables and how exceptions propagate. try/catch no longer works in a meaningful way. More complicated examples than the above clearly illustrate that futures and promises do a poor job of recovering a "straight-line code" style.

Asynchronous programming is a form of concurrent programming. They have basically the same use-cases, and in particular, they both are fine for IO-bound workloads. They are even dual to each other. And in particular related to that, there's no need for threads to be OS-level processes, hence the "lightweight" or "green" threads supported by many languages which often provide no parallelism (as in utilization of multiple cores).

  • Channel has something to do with non-blobking IO. Idea of non blocking IO completely different from asynchronous IO. – user1787812 Oct 7 '17 at 12:03
  • @user1787812 Channel was intended as a made-up "channel" notion, not java.nio.channel.Channel which isn't generic anyway. – Derek Elkins left SE Oct 7 '17 at 12:31
  • @user1787812 If you read the duality paper I linked to (or its main references with respect to the duality aspect, the Lauer-Needham duality), a blocking call is the dual of registering an (one-time) event-handler. Conversely, to make a blocking call asynchronous, you just execute it in its own thread. – Derek Elkins left SE Oct 7 '17 at 12:35
  • It is actually the callback registration style that is often seen as the cause of the "code complexity". Event loops can be used to implement state machines. While a state machine programming style causes the body of a task to become dispersed across many different methods, it does not create the visual pollution of nested lambda. Callback registration sits on top of a framework or facility (such as the "channel" in your example), whereas state machine programming style can be implemented by yourself. Meanwhile, the "event loop" is the only facility required. – rwong Oct 7 '17 at 13:46
  • @rwong The "callback registration style" is a response to the limitations and non-modularity of the "event loop style". This example written against an event loop without a "framework or facility" on top of it, would be even more ugly and complex. You would not only need pack any relevant state this function needed, in this case chan2, into the "global" state, you also need to pack the control flow state into it. – Derek Elkins left SE Oct 7 '17 at 14:03

Read this first:

Eric Lippert has already written an excellent answer on Stack Overflow that should also answer your question: What is the difference between asynchronous programming and multithreading?


Your hunch is right; multi-threading wouldn't improve the performance aspects of IO-bound tasks.

What the Java author (as quoted by OP) says is, while it wouldn't improve performance, it reduces the complexity of your code.

Different goals, different conclusions.

My answer focuses on the use of terminology and the issue of confusion.

There are several levels of "can". The first one is the least generalisable and the third one is the most generalisable.

  • That it is possible at all
    • (i.e. without considering merits and efforts)
  • That it is advantageous to do so in terms of a specific criteria
    • (e.g. able to shorten the time to completion by leveraging unused CPU cores)
  • That it is generally recommended for the given situations after a wide range of criteria have been considered
    • (i.e. there is an accompanying list of criteria considered)
    • (but, it also makes a lot of assumptions about "the general case / needs", which means someone with an unusual requirement may find it not applicable.)

When you mention "mutually-exclusive tasks", a better way to say that is tasks that can be independently executed, and does not obstruct and/or interfere (harm) each other when executed simultaneously (as in multiple CPU cores) or concurrently ("overlapped", or "asynchronously", or "task-switching").

It is always possible, and is usually advantageous, to have independently executable tasks executed over multiple CPUs. The exception to this rule is if the tasks are too small, then the overheads (mostly the wait time due to handing off tasks and results across CPUs) will ruin the savings.

Asynchronous is an adjective that roughly means punctuated. It means that a task is not continuously executing. It switches between executing and pausing (blocking). In general it is gradually progressing toward the goal (completion).

Asynchronous can refer to:

  • The coding style,
  • Syntactic and code-generation features provided by a programming language,
  • The overall manner in which a higher-level task is executed, a fact which has to be judged subjectively.

When asynchronous programming is done on a programming language that does not provide the best syntactic sugar for it, the result is more complicated code which is harder to understand, maintain, and troubleshoot. Therefore, it comes with the advice (or, a break from the general advice) that, if the code complexity is too high, then it is advisable for one to abandon the asynchronous programming attempt and fall back to a synchronous coding style.

If the language provides very good syntactic sugar for it, then a lot of programming can be done in asynchronous programming style without suffering the code complexity.

Asynchronous code is not required to be single-threaded. In fact, on C# you can combine asynchronous programming and multithreading(*), as explained in Eric Lippert's answer (see top of answer for the link).

(*) It just creates a "how do I get back to the caller" / "code executing on the wrong thread" hell. For this reason it is recommended to have one "master thread" in charge of delegating tasks, and this "master thread" will implement an event loop, which is the mechanism through which it will receive procedure invocation, communication, and completion from other tasks or worker threads.

An architecture that is asynchronous and single-threaded may sometimes be chosen because of technical limitations of one or more libraries or frameworks it uses. For example, when programming on Windows, the GUI is usually single-threaded, which means it is not thread-safe to be invoked from a thread that is not the main-thread.

The word "I/O bound" is usually a misnomer. It basically means "tasks that take time to finish yet aren't CPU-bound."

As an example, a task that is sometimes said to be "I/O bound" involves just a little bit of data transmission. For example, data is being crunched on a remote server; the local computer's task is merely waiting for the remote server saying "done".

  • It's called "I/O bound" because communicating with any system that's not bound to the current CPU generally requires I/O, even if that I/O is internal to the computer. – Robert Harvey Oct 7 '17 at 15:29
  • @RobertHarvey The point is the I/O isn't the limiting factor; the limiting factor may be the remote system's CPU, or the speed-of-light latency, or (hopefully not) the time it takes an operator to get out of bed after their cat tripped over a cable. It's not the I/O itself. – user253751 Oct 9 '17 at 4:20
  • 1
    I never really thought "I/O bound" implied that I/O was the limiting factor. Common sense dictates that you're waiting on whatever is at the other end of that I/O. – Robert Harvey Oct 9 '17 at 4:29
  • Well in the case of most sensor readings, a large IP transfer, or access to a mechanical disk then either the device doing the I/O, or the bus to that device, is the limiting factor. (When accessing a remote machine however, the part that is considered "I/O" ends just outside the local computer's network port. After that point, we can say we are waiting for something else to do I/O, rather than waiting for I/O directly) – user253751 Oct 9 '17 at 4:36
  • To put it simply, there is a difference between something that is truly "I/O bound" versus "waiting on an external event to happen". – rwong Oct 9 '17 at 6:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.