You are probably accustomed to the idea that your smartphone can run more than one app at a time. Even if you are only used to seeing just one app in the foreground, you will have noticed that some apps tend to run in background and send you notifications when something important happens: an app has been updated, you got a new email, someone mentioned you on Twitter, and so on. But the question is, how does Android manage to do lots of things simultaneously?
Before we start to dive into the world of multitasking I’d like to first suggest watching my what is a kernel video. The reason I say that is because some of the ideas we are going to be discussing here are explored in greater depth in that video/article.
OK, so first a bit of background. At the heart of your smartphone is a System-on-a-Chip. It is basically a single chip with lots of different parts to it including a CPU, a GPU, a memory controller and some other fancy bits and pieces. The CPU, in conjunction with the memory controller, uses the Random Access Memory (RAM) to store things: data, images, audio, video and various bits of state information like scores or the position of the main character in the game you are playing. Even the instructions themselves for the apps that are running are stored in RAM. Without RAM, a CPU is dead in the water.
The CPU together with the RAM are what we call system resources. The simplest form of computer (like the little microcontroller driving the display on your microwave oven) gives all of the CPU time and all of the RAM to the single program which it is running. It only has one task (show those digits, and do it well) and its life is simple. But the moment you want to run two programs, things become a lot more complicated. Now the CPU time and the RAM need to be divided up. Some for program one and some for program two. You also need to make sure that this division of system resources is done according to the right criteria. Is one program allowed to get more memory than the other? What about processor time?
There are several different techniques for dividing system resources among different programs, but we will skip over those and jump straight to preemptive multi-tasking, as that is what the Linux kernel does and that is what we get with Android.
Since the kernel needs to divide the system resources among several programs it must have a way to manage and control those programs. It can’t just say “hey, you over there, you want some CPU time!” The kernel needs to have a rock solid understanding of what is running and what needs system resources. To do that every program (i.e. app) that runs on your smartphone is contained within a process. A process has an ID, known as the PID (Process ID); a priority, how important is it; its own address space, that is its own chunk of memory; and some state information: running (or runnable), sleeping, stopped and zombied. At the lowest level there is actually loads more data associated with a process, but we don’t need to worry about those now.
Now that the kernel knows about every app that should be running, it can start to schedule different system resources for each task. If a process is sleeping then it doesn’t need anything, if it is runnable (i.e. ready to run) then it needs some CPU time and so on. The result is that a slice of the CPU’s time is given to a process, say process 232, and then a slice can be given to another process, say process 289, and so on. If process 232 is waiting for some information to come back over the network then it will sleep until the kernel wakes it up and says, “hey, your data is here, have some CPU time so you can work on the new stuff you just got.” This juggling of processes goes on at the millisecond level, very fast, and just like the frames of a cartoon, you get the appearance of smoothness and multiple programs running at once.
Finally, when an app exits, the kernel will clean up all the resources used by the app (like open files, allocated memory etc) and eventually delete the process that was created for that app. If an app needs to be forcibly exited then the process can be killed and obliterated by the kernel.
The only other thing worth mentioning here is multi-core CPUs. With multi-core CPUs the kernel can actually run more than one app at once. However the good news is that the fundamental idea of processes remains the same, but now the kernel has more CPU resources to share out among the processes.
Inside of a process is also the executable code (the instructions) for that app. The process keeps track of how far down the program it has reached and then when its slice of CPU time ends it will store that value (known as the program counter). It will start again from that point the next time it is run. And this gives us the idea of a thread, a thread of execution. By default every Linux program (and Android app) has one thread. The execution will start at the beginning of the program, run for however long needed, and then finally exit.
But what if your app needs to update the UI while loading some graphics from the storage? Or what if your app needs to upload data to a server for a MMORPG while still allowing you to play the game? Here you need two (or more) threads of execution. So a thread is a line of execution inside the app’s executable code that occurs in parallel with other threads, which are also executing the same app code.
Each thread has access to the same memory, the same files, and the same network ports. This is because each thread is part of the same process. For example, developers writing code for Android can use a class called AsyncTask. It is a quick and easy way of performing something in a separate thread. This means you can perform some network activity in one thread while continuing to update your game or UI in the main thread.
If you are an app developer it is worth noting that all AsyncTask jobs are executed on a single thread. What this means is that if you submit two or more AsyncTask jobs simultaneously they will be executed in serial. The first AsyncTask will be executed while the second and third jobs wait. When the first task is done then the second will start, and so on. If this is a problem then you need to use a pool of worker threads plus some specific named threads which do specific tasks. If you need help on setting up your worker threads then Google has some great Processes and Threads documentation. I would also recommend that you read my article about the top Android performance problems faced by app developers.
The curious among you may have spotted the danger with threads, what if two threads enter the same section of the program at the same time and try to access the same bits of memory. This is in fact a real problem and it can lead to all kinds of strange behavior. The way around it is to use locks. There are different types of locks and different methods of operation, however the basic idea is this: When a thread enters a sensitive area of code it locks that code so that other threads can’t enter it. Once it has finished it leaves the code and unlocks it, which allows another thread to enter.
Of course this can lead to a bottleneck. All the benefits of that nice multi-threading disappear as the threads queue up one behind the other to get access to a certain section of the program. There is also the problem of what happens when a thread leaves the section of code but doesn’t unlock, for example because of an error condition. Another famous locking problem can occur when thread 1 has locked some code but is waiting for thread 2 to perform a task, however thread 2 can’t perform the task because it needs to use the code that thread 1 has locked. This results in deadlock.
So the problems of concurrency and synchronization aren’t trivial, however if done right an app can perform smoothly, update the UI and talk to the network, all because it has multiple threads.
A process is the logical container used to group all the information needed to run a program (an app) on a Unix-like operating system, including Linux and therefore Android. The kernel uses the information about the process to manage the system resources including the available CPU time, so that each process gets a chance to run. But switching between processes in a timely manner, a smartphone can seem like it is running more than one app at once. Smartphones with multi-core processors use the same principle but can actually run programs concurrently.
Threads are a way for different parts of the same app, in the same process, to be run in concurrently, however using threads unwisely can create synchronization issues.