Threads

public class App {
    public static void print(String msg) {
        System.out.println(Thread.currentThread().getName() + " > " + msg);
    }
    public static void foo() {
        print("B");
    }
    public static void main(String[] args) {
        print("A");
 
        foo();
 
        print("C");
    }
}
$ javac App.java
main > A
main > B
main > C
public class App {
    public static void print(String msg) {
        System.out.println(Thread.currentThread().getName() + " > " + msg);
    }
    public static void foo(Runnable action) {
        action.run();
    }
    public static void main(String[] args) {
        print("A");
 
        foo(() -> {
            print("B");
        });
 
        print("C");
    }
}
$ javac App.java
main > A
main > B
main > C
public class App {
    public static void print(String msg) {
        System.out.println(Thread.currentThread().getName() + " > " + msg);
    }
    public static void main(String[] args) {
       print("A");
 
        Thread thread = new Thread(() -> {
            print("B");
        }, "background");
 
        print("C");
    }
}
$ javac App.java
main > A
main > C
public class App {
    public static void print(String msg) {
        System.out.println(Thread.currentThread().getName() + " > " + msg);
    }
    public static void main(String[] args) {
        print("A");
 
        Thread thread = new Thread(() -> {
            print("B");
        }, "background");
 
        thread.start();
 
        print("C");
    }
}
$ javac App.java
main > A
main > C
background > B
public class App {
    public static void print(String msg) {
        System.out.println(Thread.currentThread().getName() + " > " + msg);
    }
    public static void main(String[] args) {
        print("A");
 
        new Thread(() -> {
            print("B");
        }, "background").start();
 
        print("C");
    }
}

Threads live until their runnable’s run method returns.

Creating new Threads is expensive. We would prefer to boot some threads and keep them alive indefinitely. How do we keep a thread from returning? Loop forever.

while (true) {
 
}

Also you will commonly encounter the for loop version:

for (;;) {
 
}

https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/os/Looper.java

public class App {
    public static void print(String msg) {
        System.out.println(Thread.currentThread().getName() + " > " + msg);
    }
    public static void main(String[] args) {
        print("A");
 
        new Thread(() -> {
            for (;;) {
 
            }
        }, "background").start();
 
        print("C");
    }
}
$ javac App.java
main > A
main > C

We are now keeping background alive, but this thread isn’t very useful, it doesn’t do any work.

Our next problem is how to send background new work to do after it has already started. It’s easy to pass something to a thread when you create it—much harder to do once it starts.

Maybe we can create a member variable that both the background thread and the main thread have access to.

public class App {
    public static void print(String msg) {
        System.out.println(Thread.currentThread().getName() + " > " + msg);
    }
 
    private static Runnable backgroundAction;
 
    public static void main(String[] args) {
 
        print("A");
 
        new Thread(() -> {
            for (;;) {
                if (backgroundAction != null) {
                    backgroundAction.run();
                }
            }
        }, "background").start();
 
        backgroundAction = () -> {
            print("B");
        };
 
        print("C");
    }
}
$ javac App.java
main > A
main > C
background > B
background > B
background > B
...
public class App {
    public static void print(String msg) {
        System.out.println(Thread.currentThread().getName() + " > " + msg);
    }
 
    private static Runnable backgroundAction;
 
    public static void main(String[] args) {
 
        print("A");
 
        new Thread(() -> {
            for (;;) {
                if (backgroundAction != null) {
                    backgroundAction.run();
                    backgroundAction = null;
                }
            }
        }, "background").start();
 
        backgroundAction = () -> {
            print("B");
        };
 
        print("C");
    }
}
$ javac App.java
main > A
main > C
background > B

What happens when we schedule multiple actions in a short time period?

public class App {
    public static void print(String msg) {
        System.out.println(Thread.currentThread().getName() + " > " + msg);
    }
    public static void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (Exception e) {}
    }
 
    private static Runnable backgroundAction;
 
    public static void main(String[] args) {
 
        print("A");
 
        new Thread(() -> {
            for (;;) {
                if (backgroundAction != null) {
                    backgroundAction.run();
                    backgroundAction = null;
                }
            }
        }, "background").start();
 
        backgroundAction = () -> {
            print("B");
            sleep(1);
        };
 
        print("C");
 
        backgroundAction = () -> {
            print("D");
        };
 
    }
}
$ javac App.java
main > A
main > C
background > B

D never gets printed. Why? We assigned the action B to backgroundAction, then action B gets picked up by the thread and executed. While action B is running, we overwrite backgroundAction with action D. When action B finishes it overwrites backgroundAction with null.

Maybe we can fix that by only overwriting backgroundAction with null when the action hasn’t changed:

public class App {
    public static void print(String msg) {
        System.out.println(Thread.currentThread().getName() + " > " + msg);
    }
    public static void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (Exception e) {}
    }
 
    private static Runnable backgroundAction;
 
    public static void main(String[] args) {
 
        print("A");
 
        new Thread(() -> {
            for (;;) {
                Runnable action = backgroundAction;
                if (action != null) {
                    action.run();
                    if (action == backgroundAction) {
                        backgroundAction = null;
                    }
                }
            }
        }, "background").start();
 
        backgroundAction = () -> {
            print("B");
            sleep(1);
        };
 
        print("C");
 
        backgroundAction = () -> {
            print("D");
        };
 
    }
}
$ javac App.java
main > A
main > C
background > B
background > D

What happens if we try to schedule even more actions this way?

public class App {
    public static void print(String msg) {
        System.out.println(Thread.currentThread().getName() + " > " + msg);
    }
    public static void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (Exception e) {}
    }
 
    private static Runnable backgroundAction;
 
    public static void main(String[] args) {
 
        print("A");
 
        new Thread(() -> {
            for (;;) {
                Runnable action = backgroundAction;
                if (action != null) {
                    action.run();
                    if (action == backgroundAction) {
                        backgroundAction = null;
                    }
                }
            }
        }, "background").start();
 
        backgroundAction = () -> {
            print("B");
            sleep(1);
        };
 
        print("C");
 
        backgroundAction = () -> {
            print("D");
        };
 
        backgroundAction = () -> {
            print("E");
        };
 
    }
}
$ javac App.java
main > A
main > C
background > B
background > E

While action B was processing, we assigned action D, and immediately overwrote with action E before action D could be picked up by the thread.

Having only one place to keep a “next” action for the background thread isn’t working out. We need something that will handle multiple values, just a single value, or no values. Something like a List that we can add new items to the end, then remove them from the front. We can use a First-In, First-Out collection: a Queue.

In Java, the Queue interface has two methods we’re interested in. Queue.add() will add new items onto the end of the list. Queue.poll() will remove the first item in the list and return it.

import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
 
public class App {
    public static void print(String msg) {
        System.out.println(Thread.currentThread().getName() + " > " + msg);
    }
    public static void main(String[] args) {
        Queue<Runnable> queue = new ConcurrentLinkedQueue<>();
 
        print("items in queue: " + queue.size());
 
        queue.add(() -> {
            print("A");
        });
 
        print("items in queue: " + queue.size());
 
        queue.add(() -> {
            print("B");
        });
 
        print("items in queue: " + queue.size());
 
        queue.poll().run();
 
        print("items in queue: " + queue.size());
 
        queue.poll().run();
 
        print("items in queue: " + queue.size());
    }
}

The Queue.poll() method is very helpful here because it retrieves the next item, but also clears that item out of the list for us so each item can only ever be accessed once.

We can add a Queue to our App, and add some work to it.

import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
 
public class App {
    public static void print(String msg) {
        System.out.println(Thread.currentThread().getName() + " > " + msg);
    }
    public static void main(String[] args) {
        Queue<Runnable> queue = new ConcurrentLinkedQueue<>();
 
        print("A");
 
        new Thread(() -> {
            for (;;) {
 
            }
        }, "background").start();
 
        queue.add(() -> {
            print("B");
        });
 
        print("C");
    }
}
$ javac App.java
main > A
main > C

We now have a queue to put runnables, but we haven’t setup our thread to pick up work from that queue.

import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
 
public class App {
    public static void print(String msg) {
        System.out.println(Thread.currentThread().getName() + " > " + msg);
    }
    public static void main(String[] args) {
        Queue<Runnable> queue = new ConcurrentLinkedQueue<>();
 
        print("A");
 
        new Thread(() -> {
            for (;;) {
                Runnable action = queue.poll();
                if (action != null) {
                    action.run();
                }
            }
        }, "background").start();
 
        queue.add(() -> {
            print("B");
        });
 
        print("C");
    }
}
$ javac App.java
main > A
main > C
background > B

Now we have a way to send work from the main thread to the background thread, can we do the opposite as well? Can we send work from the background thread to the main thread?

Add a queue for the main thread, but no one is processing the main queue, so D is never printed.

Should we create a main thread? Where is the main thread?

Gateway to all Java apps is the public static void main(String[] args)method for a single class. That method is the main thread. When that thread returns, the main thread stops.

The next problem we need to solve is keeping the main thread alive by making sure the app’s main method never returns:

import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
 
public class App {
    public static void print(String msg) {
        System.out.println(Thread.currentThread().getName() + " > " + msg);
    }
    public static void main(String[] args) {
        Queue<Runnable> queue = new ConcurrentLinkedQueue<>();
 
        print("A");
 
        new Thread(() -> {
            for (;;) {
                Runnable action = queue.poll();
                if (action != null) {
                    action.run();
                }
            }
        }, "background").start();
 
        queue.add(() -> {
            print("B");
        });
 
        print("C");
 
        for (;;) {
 
        }
    }
}

Adding an infinite loop at the bottom of the main method allows the app to boot and then keeps the main thread alive indefinitely.

Now we know who our main thread is, and our main thread is kept alive, we need a way to send some work to it from another thread. Maybe we can use another queue:

import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
 
public class App {
    public static void print(String msg) {
        System.out.println(Thread.currentThread().getName() + " > " + msg);
    }
    public static void main(String[] args) {
        Queue<Runnable> bgQueue = new ConcurrentLinkedQueue<>();
        Queue<Runnable> mainQueue = new ConcurrentLinkedQueue<>();
 
        print("A");
 
        new Thread(() -> {
            for (;;) {
                Runnable action = bgQueue.poll();
                if (action != null) {
                    action.run();
                }
            }
        }, "background").start();
 
        bgQueue.add(() -> {
            print("B");
 
            mainQueue.add(() -> {
                print("D");
            });
        });
 
        print("C");
 
        for (;;) {
 
        }
    }
}

Added a second queue and now the runnable we added to the background thread queue also adds work to the main thread queue.

Next we need to process the items added the main thread queue:

import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
 
public class App {
    public static void print(String msg) {
        System.out.println(Thread.currentThread().getName() + " > " + msg);
    }
    public static void main(String[] args) {
        Queue<Runnable> bgQueue = new ConcurrentLinkedQueue<>();
        Queue<Runnable> mainQueue = new ConcurrentLinkedQueue<>();
 
        print("A");
 
        new Thread(() -> {
            for (;;) {
                Runnable action = bgQueue.poll();
                if (action != null) {
                    action.run();
                }
            }
        }, "background").start();
 
        bgQueue.add(() -> {
            print("B");
            mainQueue.add(() -> {
                print("D");
            });
        });
 
        print("C");
 
        for (;;) {
            Runnable action = mainQueue.poll();
            if (action != null) {
                action.run();
            }
        }
    }
}
$ javac App.java
main > A
main > C
background > B
main > D

Next we could extract the looping code into a utility:

import java.util.Queue;
 
public class Looper {
    public static void loop(Queue<Runnable> queue) {
        for (;;) {
            Runnable action = queue.poll();
            if (action != null) {
                action.run();
            }
        }
    }
}
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
 
public class App {
    public static void print(String msg) {
        System.out.println(Thread.currentThread().getName() + " > " + msg);
    }
    public static void main(String[] args) {
        Queue<Runnable> bgQueue = new ConcurrentLinkedQueue<>();
        Queue<Runnable> mainQueue = new ConcurrentLinkedQueue<>();
 
        print("A");
 
        new Thread(() -> {
            Looper.loop(bgQueue);
        }, "background").start();
 
        bgQueue.add(() -> {
            print("B");
            mainQueue.add(() -> {
                print("D");
            });
        });
 
        print("C");
 
        Looper.loop(mainQueue);
    }
}

The Android OS core provides a public Looper class which does something similar, although the real Android Looper is considerably more convoluted.

If we were to extract the background thread and it’s queue into another class, we will have recreated the Actor pattern:

import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
 
public class Actor {
    private Queue<Runnable> queue = new ConcurrentLinkedQueue<>();
 
    public Actor(String name) {
        new Thread(() -> {
            Looper.loop(queue);
        }, name).start();
    }
 
    public void send(Runnable action) {
        queue.add(action);
    }
}
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
 
public class App {
    public static void print(String msg) {
        System.out.println(Thread.currentThread().getName() + " > " + msg);
    }
    public static void main(String[] args) {
        Queue<Runnable> mainQueue = new ConcurrentLinkedQueue<>();
        Actor bgThread = new Actor("background");
 
        print("A");
 
        bgThread.send(() -> {
            print("B");
            mainQueue.add(() -> {
                print("D");
            });
        });
 
        print("C");
 
        Looper.loop(mainQueue);
    }
}

Once we start up the thread and give it access to the queue, we no longer require any future access to the thread itself. We only need to be able to add things to the queue, so the Actor can hide all of that internally and expose only a single public method for sending it work.

One of the cool things we can do with Thread+Queue combinations is to create multiple threads that all watch the same queue:

import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
 
public class Actor {
    private Queue<Runnable> queue = new ConcurrentLinkedQueue<>();
 
    public Actor(String name) {
        new Thread(() -> {
            Looper.loop(queue);
        }, name + "-1").start();
        new Thread(() -> {
            Looper.loop(queue);
        }, name + "-2").start();
    }
 
    public void send(Runnable action) {
        queue.add(action);
    }
}
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
 
public class App {
    public static void print(String msg) {
        System.out.println(Thread.currentThread().getName() + " > " + msg);
    }
    public static void main(String[] args) {
        Queue<Runnable> mainQueue = new ConcurrentLinkedQueue<>();
        Actor bgThread = new Actor("background");
 
        bgThread.send(() -> {
            print("A");
        });
        bgThread.send(() -> {
            print("B");
        });
        bgThread.send(() -> {
            print("C");
        });
 
        Looper.loop(mainQueue);
    }
}
$ javac App.java
background-1 > A
background-2 > B
background-1 > C

Swift provides, as a part of their Grand Central Dispatch concurrency framework, a DispatchQueue is an Actor. A DispatchQueue wraps a thread or thread pool, kept alive indefinitely using loops, and scheduling work through a queue.