Title: CS 141a Distributed Computation LaboratoryIntroduction to Threads
1Recap
- Java and Object Orientation
2Today
- Introduction to Threads
- Java Thread Mechanics
3Threads
- A thread is a call sequence that executes
independently of others while sharing underlying
system resources (memory, files, network
sockets). - Most applications today are multithreaded,
especially in the presence of graphical user
interfaces. - In order to prevent conflicts when accessing
shared resources, threads must communicate with
each other in some way. - In this way, multithreaded and distributed
systems are similar.
4Thread Libraries
- Thread libraries manage the activities associated
with threads (creation, deletion, context
switching, etc.) - Many different thread libraries
- Most OS vendors have proprietary thread
libraries, some implemented at kernel level (such
as Solaris), some not (such as FreeBSD). - POSIX threads (pthreads) - a C API - is perhaps
the most widely used, available on most UNIX (and
Linux) systems. - We will concentrate on Java threads in this
class, but basic principles are similar for most
thread libraries.
5Thread Scheduling
- On a single-processor machine, youre never
really executing two threads simultaneously -
its simulated by the thread library. - Two basic types of threading
- In cooperative threading, a thread must
(explicitly or implicitly) relinquish control
before another thread can run. - In preemptive threading, a thread can be kicked
off the processor (preempted) by another thread
at any time. - In either type, a scheduler determines which
threads will run, and when. - All the operating systems we will work with use
preemptive threading.
6Thread Scheduling
- Many different ways of scheduling threads.
Examples - Round-robin every thread in turn gets a
particular amount of processor time, whether it
needs the time or not. - Priority queue each thread is placed into a run
queue at a location dependent on its priority
attribute, executes when it gets to the head of
the queue, then is placed in the queue again. - Similar to process scheduling on UNIX - processes
have levels of niceness that determine (more or
less) how often they get processor time. - We wont worry much about thread scheduling for
the time being.
7Inter-thread Communication
- Communication among threads is essential for them
to accomplish anything useful. - Communication can be carried out in two basic
ways - Shared Memory Multiple threads read from/write
to the same memory location (variable, object,
file). - Message Passing Threads send messages to/receive
messages from other threads. - In this class we will focus almost entirely on
message passing, but well use shared objects
(queues) to implement it.
8Java Threads
- Javas thread library is unique in a number of
ways (some good, some not so good). - In Java, each thread is represented by an object
of class java.lang.Thread, which handles the
necessary bookkeeping and provides methods for
controlling the thread. - Java threads are mapped by the Java VM to either
OS (native) threads or higher-level threads
(green threads). Current (1.3/1.4) Java VMs use
native threads by default (some can only use
native threads). - Native threads are more efficient, especially on
multiprocessor systems where two threads really
can execute simultaneously.
9Java Threads
- Every Java program has at least one thread,
because one is started to run the main method. - VMs create their own additional threads, for
functionality such as garbage collection and the
AWT. - Java makes very weak guarantees about thread
scheduling it can be fully preemptive, fully
cooperative, or anything in between. - In practice, modern VMs use preemptive thread
scheduling, especially if they use native
threads.
10Java Threads
- Java threads belong to named thread groups -
each thread group is represented by an object of
class java.lang.ThreadGroup. - Each thread has a priority level (an int) - these
can affect thread scheduling on certain VMs, but
are not guaranteed to do so. - Priorities range from Thread.MIN_PRIORITY (1) to
Thread.MAX_PRIORITY (10). - Default priority is Thread.NORM_PRIORITY (5).
- Each thread starts out with a priority identical
to the priority of the thread that created it.
11Creating Java Threads
- To create a new type of Java thread, you can
basically do one of two things - Write a class that inherits from the class
java.lang.Thread. - Write a class that implements the interface
java.lang.Runnable. - In either case, you put the code you want
executed by the thread into the run method of
your new class. - It is usually better to implement Runnable than
to extend Thread, for several reasons - Extending Thread means you cant extend any other
class. - Keeping your code separate from the thread
bookkeeping code is a good thing.
12Creating Java Threads
- The Thread class has constructors that accept
various combinations of the following parameters - A Runnable object, whose run method will be
executed within the thread. - A ThreadGroup object, determining the thread
group in which the thread will be created. - A name for the thread (a String), which is used
mostly for debugging output. - After a Thread object has been constructed, it
can then be manipulated in various ways.
13Starting and Terminating Threads
- The start method - which takes no parameters -
starts the run method (either the one in the
class itself, or the one in a provided Runnable)
in a new thread. - A thread terminates when the run method returns,
either successfully or because of an unchecked
exception. - Threads cannot be restarted (attempting to do so
causes an InvalidThreadStateException). - The isAlive method returns true if the thread is
running, and false if it isnt. There is no way
to tell whether a thread that is not currently
running was ever running.
14Thread Interleaving Example
- public class Test implements Runnable
-
- private int number
- public Test(int number)
-
- this.number number
-
- public void run()
-
- for (int i 0 i lt 2 i)
-
- System.out.print
- (I am thread number number)
- System.out.println()
-
-
- public static void main(String argv)
-
- for (int i 0
- i lt Integer.parseInt(argv0) i)
-
- Thread thread
- new Thread(new Test(i))
- thread.start()
-
-
15Thread Interleaving Example Output
- One Possibility
- I am thread number 0
- I am thread number 1
- I am thread number 2
- I am thread number 3
- I am thread number 4
- I am thread number 0
- I am thread number 1
- I am thread number 2
- I am thread number 3
- I am thread number 4
- Another Possibility
- I am thread number 0
- I am thread number 0
- I am thread number 2
- I am thread number 4
- I am thread number 4
- I am thread number 3
- I am thread number 1
- I am thread number 2
- I am thread number 3
- I am thread number 1
16Thread Interleaving Example Output
- Yet Another Possibility
- I am thread number 0I am thread number 1I am
thread number 2 - I am thread number 3
- I am thread number 4I am thread number 3
- I am thread number 2
- I am thread number 1
- I am thread number 4
- I am thread number 0
17Thread Interleaving Example
- And Another
- I am thI am thrI amreeaadd numbernumber 10
thread number 2 - I am
- thread number 3
- I am thread number 4
- II aamm tthhrreeaadd numbernumber 0 1
- I am I am
- thread numthread number 3ber4
- I am thread number 2
18Daemon Threads
- Before starting a thread, you can call the
setDaemon method to make it a daemon thread. - Usually, the Java VM will exit when there are no
threads running other than its own - but it
ignores daemon threads. - You can check to see whether a thread is a daemon
thread by calling the isDaemon method. - A threads daemon-ness cannot be changed after
the thread has been started.
19Daemon Threads Example
- If we change our main method to
- public static void main(String argv)
-
- for (int i 0
- i lt Integer.parseInt(argv0) i)
-
- Thread thread
- new Thread(new Test(i))
- thread.setDaemon(true)
- thread.start()
-
-
- Our output might look like this
- I am
- Or this
- I am thread number 1.
- I am I am threthreadad
- Or this
20Thread Control - Interrupt
- Each thread has an interruption status (a
boolean). - Calling interrupt on a Thread object has one of
two effects - If the thread is sleeping or waiting, an
InterruptedException is raised in the thread and
its interruption status is set to false. - If the thread is doing anything else, its
interruption status is set to true. - A thread determines its interruption status with
the static method Thread.interrupted. - This is a very basic form of inter-thread
communication.
21Interruption Example
- public class Interruptee implements
- Runnable
-
- public void run()
-
- while (!Thread.interrupted())
-
- System.out.println
- (No interrupt yet)
-
- System.out.println
- (Finally, an interrupt!)
-
- public static void main
- (String argv)
-
- Thread thread new Thread
- (new Interruptee())
- thread.start()
- thread.interrupt()
-
22Interruption Example Output
- Output could be
- No interrupt yet
- No interrupt yet
- No interrupt yet
- No interrupt yet
- Finally, an interrupt!
- Or just
- Finally, an interrupt!
- Or anything in between
23Thread Control - Join
- Calling join on a Thread object t causes the
calling thread to suspend until the target thread
has completed. - The call returns when t.isAlive is false.
- If a timeout is specified, the call returns when
the timeout expires even if the target thread is
still running. - You should only call the join method on threads
you created - otherwise, you dont necessarily
know how they will behave.
24Join Example
- public class Joinee implements
- Runnable
-
- public void run()
-
- for (int i 0 i lt 5 i)
-
- System.out.println
- (Line number i)
-
- System.out.println
- (Thread exiting.)
-
- public static void main
- (String argv)
-
- Thread thread
- new Thread(new Joinee())
- System.out.println
- (Starting thread.)
- thread.start()
- thread.join()
- System.out.println
- (Thread terminated.)
-
25Join Example Output
- Output from this example will always be the same
- Starting thread.
- Line number 0
- Line number 1
- Line number 2
- Line number 3
- Line number 4
- Thread exiting.
- Thread terminated.
26Thread Control - Obsolete Methods
- Historically, there were other thread control
instance methods aside from join and interrupt - suspend - causes the target thread to suspend (if
it isnt already suspended). - resume - causes the target thread to resume (if
it isnt already running). - stop - forces the target thread to stop executing
by raising a ThreadDeath exception, with cleanup
of thread state. - destroy - forces the target thread to stop
executing with no cleanup. - All of these methods are deprecated, and destroy
was never actually implemented in any released VM.
27Thread Control - Static Methods
- The Thread.currentThread method returns a
reference to the Thread object representing the
current thread. - The Thread.interrupted method returns and clears
the interruption status of the current thread. - The Thread.sleep method takes as a parameter a
long number of milliseconds, and causes the
current thread to suspend for at least that
amount of time. - The Thread.yield method is a hint to the virtual
machine that if there any other runnable threads,
it should run one of them instead of the current
thread (useful primarily for cooperative
scheduling).
28Thread Groups
- As previously mentioned, every thread belongs to
a thread group represented by an object of class
ThreadGroup. - The primary reason for thread groups is security
- Prevent threads from interrupting threads in
other groups. - Prevent threads from setting their priorities too
high (this only matters in VMs where the
scheduler cares about priorities) - If a thread dies because of an uncaught
exception, you can find out what the exception
was by calling uncaughtException on its
ThreadGroup. - You probably wont use thread groups much.
29Synchronization
- As weve seen, bad things can happen if certain
resources are accessed concurrently by multiple
threads. - Java has constructs for synchronization that,
when used properly, can ensure that only one
thread is accessing a particular object at any
given time. - The basic synchronization construct in Java is
the lock, and the Java keyword synchronized is
used to manipulate locks. - There are two ways to use the synchronized
keyword block synchronization and method
synchronization
30Block Synchronization
- The syntax for block synchronization is
- synchronized (object-reference)
-
- // I hold the lock on object object-reference.
-
- Block synchronization allows you to lock any
object, anywhere in your code. - The most common usage is to lock this.
31Block Synchronization Example
- public class BlockSync
-
- private static Object lock
- new Object()
- private int number
- public void run()
-
- for (int i 0 i lt 2 i)
-
- synchronized (lock)
-
- System.out.print
- (I am thread number
- number)
- System.out.println()
-
-
-
- public BlockSync(int number)
-
- this.number number
-
- public static void main(String argv)
-
- for (int i 0
- i lt Integer.parseInt(argv0) i)
-
- Thread thread
- new Thread(new BlockSync(i))
- thread.start()
-
-
-
32Block Synchronization Example Output
- The only output possibilities are those where
lines are output unbroken (I am thread number
0, etc.), because all the threads are locking
the same object (lock) before writing to
System.out. - We could also have eliminated the lock variable,
and written the synchronized block as - synchronized (BlockSync.class)
-
- // body of block
-
- This works because there is one object of class
Class in the VM for each Java class.
33Method Synchronization
- The syntax for method synchronization is to add
the keyword synchronized at the beginning
(anywhere in the modifier list) of a method
declaration, such as - synchronized Object getObjectAt(int position)
- / body /
- Method synchronization is exactly equivalent to
block synchronization of the entire method on
this - Object getObjectAt(int position)
- synchronized (this) / body /
34Method Synchronization
- The synchronized keyword is not part of a
methods signature. This has two important
effects. - When you override a synchronized method in a
child class, the new method is not automatically
synchronized - you must use the keyword again. - The superclass method remains synchronized, so if
you call it with super.foo(), the synchronization
behavior is as expected. - Methods in interfaces cannot be declared with the
synchronized modifier.
35Acquiring and Releasing Locks
- Locks are acquired and released according to a
built-in protocol, and the only way to access
them is with the synchronized keyword. - All locking is based on blocks - a lock is
acquired when a synchronized block or method is
entered, and released when it is exited (or in
other special cases which will be discussed next
class). - Locks operate per-thread, so if a thread already
holds a lock for a particular object and hits
another synchronized block for that object, it
doesnt suspend. This is called reentrant locking.
36Acquiring and Releasing Locks
- A synchronized method or block obeys the locking
protocol only with respect to other synchronized
methods or blocks with the same target object. - In particular, this means that if an object has
both synchronized methods and normal methods, the
normal methods may be executed at any time (even
if a thread is executing a synchronized method). - There is no way to discover what thread holds the
lock on a particular object, or what objects are
locked by a particular thread.
37Locks and Statics
- Locking an object doesnt have any effect on
access to static fields or methods of that
objects class. - Access to static fields can only be protected
using synchronized static methods or blocks,
since fields cannot be synchronized. - Synchronization on static methods and blocks
acquires and releases the lock for the Class
object associated with those methods and blocks.
38Next Class
- More on Synchronization
- Homework 2 Overview