1.29k likes | 1.42k Views
Chapter 4 Threads. 4.1 Overview. This is what a thread has of its own: A thread id A program counter A register set (set of register values) A stack The stack contains local variables Therefore, at run time, the value of each variable in shared code can differ among threads.
E N D
4.1 Overview • This is what a thread has of its own: • A thread id • A program counter • A register set (set of register values) • A stack • The stack contains local variables • Therefore, at run time, the value of each variable in shared code can differ among threads
This is what it shares with other threads belonging to the same process: • A code section • A data section • The data section contains global variables • Therefore, at run time, each thread has access to a common set of global variable values • Other resources, such as open files
A traditional process with only one thread of control may be known as a heavyweight process • In a system that supports multi-threaded execution of common code, the threads may be known as lightweight processes.
Motivation • Multi-threading is a way of avoiding the overhead of creating full new (heavyweight) processes • It is a way of allowing multi-tasking within a single (heavyweight) process
Examples • A word processor might support concurrent text entry and spell checking by having each run as a separate thread. • A Web server may receive many requests for service—all essentially the same. • Rather than creating a separate process for each, it may spawn threads of the same request-handling code. • O/S kernels may also be multi-threaded. • Solaris does interrupt handling with threads.
Benefits • By definition, threads allow resource sharing • Threads decrease the overhead of process creation—the creation of threads is less computationally demanding • Threads introduce a form of concurrency, promoting efficient use of resources and system responsiveness • Threads may be used in multi-processing, where separate threads run on each processor, rather than separate processes
The book gives this list of benefits: • 1. Responsiveness • 2. Resource sharing • 3. Economy • 4. Scalability
Multi-Core Programming • On a multi-core chip, each core appears as a separate processor to the O/S • Multi-threading can be applied directly in a multi-core environment • Each thread can run in parallel, (simultaneously, not concurrently) on one of the cores
Challenges of Parallel Processing • This sounds nice, but the challenge of parallel programming has always been how to break the problem into pieces and put the partial solutions together at the end. • In order to get the best use out of multiple cores, both O/S code and application code have to be (re-) written to take advantage of parallelization.
The book defines the challenge in greater detail under five points: • 1. Dividing activities (as mentioned already) • 2. Balance (part of dividing): Splitting the problem evenly so each part is significant enough to have its own thread/core • 3. Data splitting: The point here is whether data can or needs to be split among threads
4. Data dependency • This is the devilish part of data. • You may be able to split the code, but the code shares data • This means that a correct solution will require synchronization on the data
5. Testing and debugging • This is a nightmare with parallel code • We will get a taste of how hard this problem must be when we consider debugging of synchronized (or incorrectly synchronized) code
The book ends the section on multiple cores with observations along these lines: • In effect, parallel programming is becoming a common, rather than a rare challenge • In order for software to make effective use of multiple cores new approaches to code design may be needed in the future • Parallel programming techniques do exist—but they are currently a specialty that is not widely known or taught
4.2 Multi-threading Models • User and kernel threads • Threads can be supported at the user level in the layered diagram of system software • Threads can also be supported directly in the kernel of the O/S
Wherever they are implemented, context switching between threads is necessary. • This is less expensive than switching between processes. • If user level threads are supported which differ from kernel level threads, there has to be a mapping between the user level threads and the kernel level threads or processes.
In other words, user level threads are assigned to kernel level threads. • The kernel level threads become analogous with a processor, and user level threads are “scheduled” to run on them.
The Many-to-One Model • Many user level threads share (are concurrently executed by) a single kernel level thread • Solaris Green threads and GNU portable threads implement this model
Characteristics of This Model • Efficiency: Thread management is handled in user space • Blocking: Any one blocking call by a user thread will cause all of the threads to block • Parallel processing: This model is not applicable in this environment. • There is only one underlying kernel thread • That thread can only use one processor
The One-to-One Model • Each user level thread is mapped to a single kernel level thread • Linux and Windows 95/98/NT/2000/XP implement this model
Characteristics of This Model • Efficiency: Each user thread has the overhead of kernel thread creation. • Most systems put an upper bound on the number of threads allowed • Blocking: One thread can’t block the others • Parallel processing: This model fits the requirements • Each kernel thread = one user thread can run on a separate processor
The Many-to-Many Model • Multiple user level threads map to a set, possibly smaller, of kernel threads • The system interactively maps user to kernel threads when scheduling • This is more flexible than the other models, but also more complex
The term “two level model” describes a system with m-to-n mapping plus the ability to map one user thread to a single kernel thread • The two level model is the most general model • IRIX, HP-UX, and Tru64 Unix implement the two level model
Characteristics of This Model • Efficiency: The system can allocate as many kernel threads as is practical or optimal • Blocking: There is no blocking • Parallel processing: This supports parallelizing a number of kernel threads up to the number of processors • Each kernel thread can run on a separate processor
4.3 Thread Libraries • A thread library provides an API for creating and using threads • There are two main non-virtual machine thread libraries • The POSIX (Unix) Pthread library supports either user or kernel threads • The Win32 threads library supports kernel threads • The details of these subsections will not be covered
Linux also supports the thread concept, although it uses different terminology • Java threads are implemented on top of whatever O/S thread environment the JVM is installed on
4.4 Java Threads • These overheads will try and follow the O/S book’s examples • If you find that the presentation of the mechanics of threading is sketchy, I recommend the following: • Go to the CS 202 Web page • Find Unit 29 there, which is not covered in CS 202 anymore • Take a look at the overheads/note file/example programs to get a clearer introduction to threads, based on building blocks that you covered in CS 202
Threads are fundamental to the Java model • A single program runs as a single thread if it doesn’t explicitly create threads • Syntax exists to create separate threads in a program
The idea is that if the machine is virtual, why not support virtual processes? • This allows the use of multi-programming/multi-tasking when writing code at the user level
Java threads don’t fit the user level vs. kernel level thread API library distinction very well • In Java, threads are supported in the Java language API • They are actually implemented in the JVM, which relies on the underlying system
One of the biggest challenges to the use of Java threads is understanding that their behavior and scheduling depends in part on the behavior and scheduling of threads as defined in the underlying system.
Threaded Java code may be formally correct, meaning that it is correctly synchronized, but its run-time behavior may differ in different environments. • This is due to the different handling of system level threads in those environments, which the JVM threading mechanism is built on top of.
Java Thread Creation • This section is mostly about syntax • Learning about the syntax is important if you want to use Java threads yourself • It’s also helpful because it makes it possible to consider example programs provided by the authors • What threads are may become clearer if you can understand what an actual threaded program is and how it works
First Approach to Writing a Threaded Application • Write a class that extends the Thread class in the Java API • Override the run() method in that class • It is the run() method which contains the program logic which is to be threaded
In a program, construct an instance of the class that extends the Thread class • Call the start() method on that object • The start() method allocates memory and initializes a new thread in the JVM
The start() method contains a call to the object’s run() method • The user program shouldn’t call the run() method directly because the initialization and allocation steps would be missed • This first approach works fine in simple cases
Observe that since a class can only extend one other class, if your class extends the Thread class, it can’t be a subclass of any other class • For example, an applet is created by extending the JAppletclass. • This approach won’t allow you to make a threaded applet
This suggests that there has to be an interface based solution to the problem • That will be the second approach • There is an explanation for why the simple approach is not the best approach conceptually
Ideally, when defining one class to be a subclass of another, the subclass should be a “kind of” the superclass • Extending the Thread class in order to override the run() method doesn’t make a new kind of Thread class • What it does is create a class which can make use of threading
The Second Approach to Writing a Threaded Application • Write a class that implements the Runnable interface • That interface specifies a run() method and nothing more • Implement a run() method in your class
You can then create a thread based on your class using this mechanism: • Construct an instance of your class • Construct an instance of the Thread class, using the constructor which takes as a parameter a reference to an object which implements the Runnable interface. • Pass an object of your class as the parameter. • Call the start() method on this instance of the Thread class
Think back to the first solution for a moment. • For what it’s worth, note that in the Java API, the Thread class implements the Runnable interface • The Thread class has a run() method, and therefore meets the requirements for being Runnable • In a sense, the first solution was a special case of the second solution • The key requirement for something to be able to run independently is that it implement the Runnable interface (the run() method)
When using the second approach, the Thread class constructor takes the runnable object and “wraps” it into a thread • There are no special requirements for the constructors of the runnable class itself. • A default constructor may work; • Constructors that take parameters may be needed. • It’s application dependent
An Example Threaded Program • The book’s example program is based on the idea of summing the first n integers • It will be covered in this way • By explaining the general idea behind the implementation • By pointing out some of the syntactical details • And then by looking at the code overall
Recall the previous example on producers and consumers in chapter 3 • The authors wanted to illustrate a concept using Java code • However, in order to do so completely correctly, it would have been necessary to use syntax that hadn’t been explained yet
Therefore, the example illustrated the general idea but with several key pieces missing • At the same time, the authors did feel compelled to use the keyword “volatile” in their code • If the code wasn’t correctly threaded or synchronized anyway, it wasn’t clear why that particular detail did have to be included.
This example has similar problems. • It still doesn’t do everything correctly • But it does include other stuff which is advanced and, from the point of view of understanding the concept they’re trying to illustrate, extraneous…
The point now is really just to see how threads can be made and used • The example they’ve chosen, if it were really amenable to a threaded solution, would require synchronization
They aren’t ready to tackle synchronization yet, so instead, they introduce the syntax for making one thread wait on another • In order to understand the example, it will be necessary to go through the thread waiting syntax, as well as the thread creation and usage syntax
The purpose of the program is to find the sum of the integers less than or equal to some given upper limit • The overall structure of the program consists of a driver, which does input and output, and a run() method in another class which does the summing • The driver and the run() method run as separate threads