Java threading introduction Thread-safety Thread methods Interruption Thread scheduling Context switching Thread priorities sleep() yield() Deadlock Threading with Swing invokeLater() Thread pools CoundDownLatch ThreadPoolExecutor CyclicBarrier
How threads work: more details
In our introduction to Java threads, we showed the basics of how to start a thread, and the idea that threads let us run multiple tasks or "mini-programs" in parallel. But to understand certain thread programming issues in more detail, it's helpful to take a more detailed look at what threads actually are and how they work.
Figure 1: Typical relationsip between
Threads and processes
A thread is essentially a subdivision of a process, or "lightweight process" (LWP) on some systems. A process is generally the most major and separate unit of execution recognised by the OS. The typical relationship between processes, threads and various other elements of the OS are shown in Figure 1 opposite. This shows two processes, each split into two threads (a simplistic situation, of course: there will be typically dozens of processes, some with dozens or more threads).
Crucially, each process has its own memory space. When Process 1 accesses some given memory location, say 0x8000, that address will be mapped to some physical memory address1. But from Process 2, location 0x8000 will generally refer to a completely different portion of physical memory. A thread is a subdivision that shares the memory space of its parent process. So when either Thread 1 or Thread 2 of Process 1 accesses "memory address 0x8000", they will be referring to the same physical address. Threads belonging to a process usually share a few other key resources as well, such as their working directory, environment variables, file handles etc.
On the other hand, each thread has its own private stack and registers, including program counter. These are essentially the things that threads need in order to be independent. Depending on the OS, threads may have some other private resources too, such as thread-local storage (effectively, a way of referring to "variable number X", where each thread has its own private value of X). The OS will generally attach a bit of "housekeeping" information to each thread, such as its priority and state (running, waiting for I/O etc).
The thread scheduler
There are generally more threads than CPUs. Part of a multithreaded system is therefore a thread scheduler, responsible for sharing out the available CPUs in some way among the competing threads. Note that in practically all modern operating systems, the thread scheduler is part of the OS itself. So the OS actually "sees" our different threads and is responsible for the task of switching between them2. The rationale for handling threading "natively" in the OS is that the OS is likely to have the information to make threading efficient (such as knowing which threads are waiting for I/O and for how long), whereas a software library may not have this information available. In the rest of our discussion, we'll generally assume this native threads model.
Next: scheduling and its implications for Java
On the next pages, we look at
1. Things are usually a little more complex, in fact. For example, a memory
address can actually be mapped to something that isn't memory (such as a portion of a file,
or device I/O). For this reason, the term address space is often preferred.
Copyright © Javamex UK 2016. All rights reserved.