Upload
weldon
View
37
Download
0
Tags:
Embed Size (px)
DESCRIPTION
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. - PowerPoint PPT Presentation
Citation preview
1
Chapter 4 Threads
2
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
3
• 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
4
• 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.
5
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
6
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.
7
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
8
• The book gives this list of benefits:• 1. Responsiveness• 2. Resource sharing• 3. Economy• 4. Scalability
9
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
10
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.
11
• 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
12
• 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
13
• 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
14
• 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
15
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
16
• 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.
17
• 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.
18
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
19
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
20
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
21
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
22
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
23
• 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
24
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
25
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
26
• 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
27
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
28
• 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
29
• 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
30
• 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
31
• 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.
32
• 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.
33
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
34
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
35
• 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
36
• 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
37
• 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 JApplet class.
• This approach won’t allow you to make a threaded applet
38
• 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
39
• 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
40
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
41
• 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
42
• 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)
43
• 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
44
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
45
• 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
46
• 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.
47
• 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…
48
• 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
49
• 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
50
• 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
51
• Because the run() method in the Runnable interface specification is void, it is not possible for it to return a computed value
• This can be overcome by passing a reference to an object where the result of the computation is stored in that object
52
• The authors choose to name the class which holds the result of the computation MutableInteger
• This name indicates that it would not be possible to pass an instance of the system supplied Integer class, since objects of that class are immutable
53
• Although, in theory, a principal advantage of threading is that the threads run concurrently, this introduces the potential for synchronization problems
• These problems have not been addressed yet, so the authors avoid them in this way:
• They use syntax which requires that after starting the summation thread, the thread of the driver has to wait for it to complete
54
• From the point of view of clarity of the example, this has two disadvantages:– It’s necessary to introduce the syntax for making one
thread wait on another– Conceptually, it results in a threaded program whose
behavior could more easily have been accomplished by non-threaded code
• In any case, the example does result in code which is threaded and does not have lurking synchronization issues
55
• The name of the method that causes one thread to depend on another is join()
• If the main() method (thread) constructs and starts another thread, a call to join() on that other thread will cause the main() method to depend on it
• The call to join() has to occur in a try block because it can throw an exception
56
• The authors introduce one more thread concept which isn’t directly relevant to the example
• There are daemon threads and non-daemon threads
• For all practical purposes, you can consider user threads to be non-daemon threads
• There is no need to worry about daemon threads or the syntax for making a thread a daemon thread
57
• The example code follows• Pay particular attention to how a thread is
created and started• Incidentally note the use of join()
58
• class MutableInteger• {• private int value;
• public int get() {• return value;• }
• public void set(int sum) {• this.value = sum;• }• }
59
• class Summation implements Runnable• {• private int upper;• private MutableInteger sumValue;
• public Summation(int upper, MutableInteger sumValue) {• if (upper < 0)• throw new IllegalArgumentException();
• this.upper = upper;• this.sumValue = sumValue;• }
• public void run() {• int sum = 0;
• for (int i = 0; i <= upper; i++)• sum += i;
• sumValue.set(sum);• }• }
60
• public class Driver• {• public static void main(String[] args) {• if (args.length != 1) {• System.err.println("Usage Driver <integer>");• System.exit(0);• }
• MutableInteger sumObject = new MutableInteger();• int upper = Integer.parseInt(args[0]);•• Thread worker = new Thread(new Summation(upper, sumObject));• worker.start();• try {• worker.join();• } catch (InterruptedException ie) { }• System.out.println("The value of " + upper + " is " +
sumObject.get());• }• }
61
Java Thread States
• Note the parallel between threads and processes– Java threads are like processes at the user level– Processes have states– Threads also have a life cycle that can be
described with states
62
List of Java Thread States
• New• Runnable• Blocked• Waiting• Timed Waiting• Terminated
63
New
• Results from the call to construct a new() instance of a Thread
64
Runnable
• Calling start() allocates memory for a thread object
• When run() is called, a thread enters the runnable state
• Java doesn’t distinguish between runnable and running.
• A running thread is in the runnable state. • Other threads may be in the runnable state but
not currently running
65
Blocked
• This happens if a thread issues a command that causes blocking
• Previously, the classic example that would be given was I/O
• Another example that used to be given was a call to such a method as sleep()
• The book currently gives as the canonical example waiting to acquire a lock (a synchronization concept)
66
Waiting
• This means waiting for an action by another thread
• If thread 1 calls join() on thread 2, thread 1 will wait for thread 2 to terminate before continuing
67
Timed Waiting
• This is similar to waiting, but with a maximum wait time telling when the wait expires
• join() can be called with a time parameter• This prevents indefinite waiting in case thread
2 doesn’t terminate in a timely fashion• It is not clear whether sleep(), for example,
now falls into this category
68
Terminated
• A thread enters the terminated state when execution reaches the end of the run() method
• Note the difference between this and garbage collection when there is no longer a reference to a thread object
• In theory, a thread can terminate successfully and then be run() again by another call somewhere in the running code.
69
A Diagram of Thread States and Example Transitional Conditions
70
The JVM and the Host O/S
• The states given above are thread states as they exist in the JVM
• These states do not necessarily agree with any particular lightweight or heavyweight system thread which the Java thread is running on
• Note that it may be difficult or impossible for the programmer/user to determine the relationship between a Java thread and the native environment thread or process that it is running on.
71
• The Java specification does not say how Java threads are to be mapped to system threads
• Thread mapping is up to whoever does the implementation for Java for a given environment (note that in theory there could be more than one implementation even for the same environment)
• Windows XP, for example, does one-to-one mapping• Unix type systems may do many-to-one or many-to-
many mapping
72
• There are no thread scheduling requirements (other than correctness) in the Java specifications.
• Threaded Java applications can vary in their behavior in different environments
73
• There are Java API methods that make it possible to check the status of a Java thread:– isAlive(): returns true if the thread has not yet
terminated– getState(): returns one of the Java thread states
listed above.
74
A Multi-threaded Solution to the Producer-Consumer/Mailbox Problem
• This takes the message passing example of the last chapter one step closer to reality
• It uses this code, already given:• The Channel interface• The MessageQueue class. – Remember that this contained an instance of a
vector and implemented an unbounded buffer
75
• The example program overall contains four other classes
• It makes use of messages that contain dates that are instances of the Java API Date class
• The four classes and their contents are outlined below
76
• Factory– Create the mailbox– Create the producer thread, passing it a reference
to the mailbox– Create the consumer thread, passing it a reference
to the mailbox– Start both threads
77
• Producer– Run in a loop– Sleep a while– Create a message– Put it in the mailbox– Print a message saying so
78
• Consumer– Run in a loop– Sleep a while– Retrieve a message or null if there isn’t one (this is
non-blocking)– Print a message saying so
79
• The SleepUtilities class– Sets a given sleeping time– Calls the Thread class sleep() method– Note that try/catch blocks are needed for various
calls which are brought together in the code for sleep()
80
Final Notes on the New Code
• This example is more complete than given in the last chapter.
• It is threaded.• However, it is still not complete. • Once two threads share (a reference to) an object,
there is a concurrency control or synchronization issue.• This code does not deal with that issue explicitly. • The issue will be discussed in detail in a future chapter.
81
• public interface Channel• {• /**• * Send a message to the channel.• * It is possible that this method may or may not
block.• */• public abstract void send(Object message);• • /**• * Receive a message from the channel• * It is possible that this method may or may not
block.• */• public abstract Object receive();• }
82
• import java.util.Vector;• • public class MessageQueue implements Channel• {• private Vector queue;
• public MessageQueue() {• queue = new Vector();• }• • /*• * This implements a non-blocking send• */• public void send(Object item) {• queue.addElement(item);• }• • /*• * This implements a non-blocking receive• */• • public Object receive() {• if (queue.size() == 0)• return null;• else • return queue.remove(0);• }• }
83
• public class Factory • { • public Factory()• {• // first create the message buffer • Channel mailBox = new MessageQueue();• • // now create the producer and consumer threads• Thread producerThread = new Thread(new Producer(mailBox));• Thread consumerThread = new Thread(new Consumer(mailBox));• • producerThread.start();• consumerThread.start(); • }
• public static void main(String args[]) { • Factory server = new Factory();• }• }
84
• import java.util.*;
• class Producer implements Runnable• {• public Producer(Channel m)• {• mbox = m;• } • • public void run()• {• Date message;• • while (true) { • SleepUtilities.nap(); • message = new Date(); • System.out.println("Producer produced " + message);• // produce an item & enter it into the buffer• mbox.send(message);• }• }• • private Channel mbox;• }
85
• import java.util.*;
• class Consumer implements Runnable• {• public Consumer(Channel m) { • mbox = m;• }• • public void run() {• Date message;• • while (true)• {• SleepUtilities.nap();
• // consume an item from the buffer• System.out.println("Consumer wants to consume.");• message = (Date)mbox.receive();• if (message != null)• System.out.println("Consumer consumed " + message);• }• }• • private Channel mbox;• }
86
• /**• * Utilities for causing a thread to sleep.• * Note, we should be handling interrupted exceptions• * but choose not to do so for code clarity.• */
• public class SleepUtilities• {• /**• * Nap between zero and NAP_TIME seconds.• */• public static void nap() {• nap(NAP_TIME);• }
• /**• * Nap between zero and duration seconds.• */• public static void nap(int duration) {• int sleeptime = (int) (duration * Math.random() );• try { Thread.sleep(sleeptime*1000); }• catch (InterruptedException e) {}• }
• private static final int NAP_TIME = 5;• }
87
4.5 Threading Issues
• What happens when you have threads on top of processes?
• What should the effect of (process) calls like fork() and exec() be, when made in a program that is running multiple threads?
• This is just more “bulleted list” stuff.• In other words, as more different ideas are
presented, the possible relationships between them are points to ponder.
88
• Q: In a multi-threaded program, if one thread calls exec(), should the whole process, all of the threads, be replaced, or just the calling thread?
• A: exec() typically replaces the whole process• exec() is a process level call, and its effects are
at the process level, not the thread level, even though, by definition, in a threaded program the call has to originate in a thread.
89
• Q: In a multi-threaded program, if one thread calls fork(), should all of the threads be duplicated, or just the calling thread?
• A: Taking Unix as an example, some versions of that O/S have two different versions of fork()
• One version duplicates all of the threads, the other duplicates just the calling thread
90
• Which version of fork() to use depends on whether or not you plan on using exec() in the forked code
• It’s more expensive to duplicate all threads rather than just one thread
• If you’re writing code where you’re going to call exec() after fork(), use the fork() that just duplicates the one thread because they’re all going to get wiped out anyway.
91
• If you’re not going to call exec(), use the fork() that duplicates all of the threads.
• If you’re not replacing the running process, you want duplicates of all of its components
92
Thread Cancellation
• This term refers to making a call that will terminate a running thread before it has naturally come to the end of run()
• You have one piece of code, or thread, which contains a reference to another thread, the target thread
• The one piece of code can cancel the target thread by making a call on it
93
Two Models of Cancellation
• Asynchronous cancellation (literally, cancellation without synchronization): – The one thread immediately terminates the target
• Deferred cancellation:– The one thread sets a parameter or makes a call
signaling that the target should be cancelled– The target is coded to periodically check its status
and to terminate itself if it has been signaled to cancel
94
• Asynchronous cancellation can lead to concurrency problems
• If the target shares resources with another thread or if it holds resources allocated by the system, asynchronous/immediate cancellation may:– Leave the resources in an inconsistent (undesirable)
state– Make it impossible for the system to reclaim the
thread’s resources
95
• In Java, the stop() call implements asynchronous cancellation.
• Code written in the past may have used it. • It is now deprecated due to these
synchronization problems.
96
The Logic of Deferred Cancellation
• The target thread checks to see if it has been signaled to terminate
• If so, it runs housekeeping code which leaves resources in a consistent state
• Then it exits• In Java, in the canceling thread, deferred
cancellation is triggered by this call: • targetThread.interrupt();
97
• In the target code there is a choice between two different calls:
• me.interrupted()• This returns true or false and clears the
interrupted status• me.isInterrupted()• This returns true or false and doesn’t clear the
interrupted status
98
• Code for an Interrupter class and an InterruptibleThread class follow
• The code for the InterruptibleThread class has been modified slightly from the book’s example
• Note that the InterruptibleThread class implements Runnable rather than extending Thread.
• This means you have to call the static method Thread.currentThread() in order to get a reference to yourself
• This is sort of a “who am I” call.
99
• public class InterruptibleThread implements Runnable• {• /**• * This thread will continue to run as long• * as it is not interrupted.• */• private boolean keepOnRunning = true;• public void run()• {• while (keepOnRunning)• {• /**• * do some work for awhile• */
• if (Thread.currentThread().isInterrupted())• {• System.out.println("I'm interrupted!");• keepOnRunning = false;• }• }• // clean up and terminate (housekeeping)• }• }
100
• public class Interrupter• {• public static void main(String[] args)• {• Thread worker = new Thread (new InterruptibleThread());• worker.start(); • // now wait 3 seconds before interrupting it• try• { • Thread.sleep(3000);• }• catch (InterruptedException ie)• {• }• worker.interrupt();• }• }
101
Another Logical Puzzle Typical of Operating Systems Stemming from this Example
• A blocked thread can’t run, so it can’t check its status
• A thread blocked for I/O, for example, won’t check its status until the I/O finishes
• If it is desirable to interrupt blocked processes in Java, suitable methods can be found in the API under java.nio
• The standard methods in java.io won’t work
102
Signal Handling
• Like the discussion of fork() and exec(), this section is Unix related
• In Java, you get to understand threading at the user level
• In Unix, it is possible to work directly with processes and system threads
• Interactions that could occur in that environment become a consideration when discussing what threads are and how they work
103
• In Unix, the occurrence of events is signaled like a software interrupt
• When a signal is received, it has to be handled• The question is, what happens when a multi-
threaded process is signaled? • Which of the threads is signaled?
104
Synchronous and Asynchronous Signals
• Synchronous – If a running process is the immediate cause of the
signal generation, the signal is sent to that process.
– Examples are division by 0 and illegal memory access
105
• Asynchronous – These are signals generated by events external to
the running process. – Examples are the expiration of a timer or pressing
CTRL+C
106
• The four examples of signals given above would cause termination.
• It is coincidental that these commonplace examples lead to termination
• In general this doesn’t have to be the case when handling signals
107
• Any event may be handled by one of two possible types of handlers– A default signal handler—system code run by the
kernel– A user-defined signal handler may be provided
• In a single-threaded process, signal delivery is clear:
• The signal is delivered to the one process in question
108
Four Choices for Multi-threaded Signal Handling
• 1. Deliver the signal to the one thread to which it applies (see the concrete example below)
• 2. Deliver the signal to every thread in the process (see the concrete example below)
• 3. Deliver the signal to certain threads in the process
• 4. Deliver the thread to a thread which has been designated to receive all of the signals for the process
109
Example of 1
• For synchronous threads, it makes sense to deliver the signal to the thread which caused the signal to be generated.
• If one thread of many divided by 0, that thread should be signaled
110
Example of 2
• CTRL+C in Unix is interpreted to mean “stop the process.”
• Thus, in a multi-threaded application, this should generate a signal to all threads to stop
111
The General Plan of Action in Unix
• The Unix command kill() is used to send signals.
• Think of kill() as meaning interrupt, not terminate
• Individual threads can be set to block or not block signals of different kinds
112
• Signals are sent to/received by the first available thread that is not blocking that kind of signal
• Sending a signal to one (appropriate) thread is sufficient since signals only have to be handled once
• Windows doesn’t use signals, but it has a facility that can accomplish the same thing.
• This fact and the details of the facility are not important
113
Thread Pools
• If a system is trying to run too many processes, performance can suffer
• Process creation can be expensive• One of the motivations for threads was to save
on the expense of full-scale process creation
114
• The same arguments apply to threads• Running too many threads can affect
performance• In the long run, the expense of thread creation
is also pure overhead cost
115
• The overall problem is one of continuously creating things, destroying them, and creating new ones again
• A thread pool is a solution to this problem• The idea is to create a collection of threads in
advance• When a task arrives in the system which can be
handled by a thread, one from the pool is assigned to that task
116
• If no free thread is available, the task has to wait
• When the task is finished, the thread is returned to the pool, not destroyed
• This is both a reasonably simple and reasonably clever idea for managing system resources
• An example of its use is in servicing Web requests
117
• It would take some effort to come up with a good algorithm to match the thread pool size to the workload on a system at any given time
• The book gives a bunch of details on thread pools.
• This includes the Java syntax for creating thread pools and executing threads in a pool
• None of those details are of importance at this time.
118
Thread Specific Data
• The default condition for threads is that they share data.
• This is one of the reasons they are advantageous.
• This means that by default, simple threads don’t have data of their own
119
• It may be desirable for threads to keep track of data items of their own.
• An example might be assigning a thread the id of a task it’s been assigned to do (like when giving a thread in a pool a task)
120
• It is possible to give threads their own data items using known syntax
• For example, the thread class could be extended with an instance variable added
• Or a class that implements the Runnable interface could have an instance variable and get() and set() methods, and the run() method might affect the value of the variable
121
• The previous solutions don’t work in the thread pool scenario, where the user doesn’t create the thread that does the task
• Java has a ThreadLocal class that can be used to declare data items that will be distinguished by the system according to which thread is running them/which thread they belong to
122
• The book gives an example, but it doesn’t show the solution to the pool problem.
• It illustrates the new syntax by solving a problem more easily solved using techniques mentioned earlier
• There is no reason to pursue the details of this example.
123
Scheduler Activations
• This is a discussion of issues involved in the many-to-many model of mapping user threads to kernel threads
• Here is a layered diagram– User thread– Lightweight process– Kernel threads– Hardware
124
• The lightweight process is what the threading system presents to the user level thread library
• It can be thought of like a virtual processor that a user level thread can be scheduled on
• Communication between the kernel and user level thread library is done by means of upcalls and upcall handlers
125
• An upcall is like a signal (or interrupt) to the thread library
• An upcall handler is like an interrupt handler. • In this case it can be thought of as scheduler
code• The kernel sends an upcall when a lightweight
process has become available for a thread to be scheduled on
126
• At the beginning of a system run, many lightweight processes may be free
• After a system has begun running, the classic cause of an upcall is that a thread which has already been scheduled on a lightweight process has issued a blocking call.
• At that point the thread enters a waiting state and the lightweight process becomes available for scheduling
127
• I am not going to go into detail on this topic.• The reason is that these are fundamentally
scheduling questions.• Scheduling is the topic of the next chapter.• It doesn’t cover thread scheduling in
particular, but that is the place where all of the details of scheduling that you will need to master are given.
128
4.6 Operating System Examples
• Skip
129
The End