520 likes | 681 Views
C. ertificación en. J. AVA. Universidad Nacional de Colombia Facultad de Ingeniería Departamento de Sistemas. Objectives Thread Fundamentals Controlling Threads Monitors, wait() and notify(). 7. THREADS. Objectives. Write code to define, instantiate, and start new threads using
E N D
C ertificación en J AVA Universidad Nacional de Colombia Facultad de Ingeniería Departamento de Sistemas
Objectives • Thread Fundamentals • Controlling Threads • Monitors, wait() and notify() 7. THREADS
Objectives • Write code to define, instantiate, and start new threads using • both java.lang.Thread and java.lang.Runnable • Recognize conditions that might prevent a thread from • executing • Write code using synchronized, wait, notify, and notifyAll • to protect against concurrent access problems and to • communicate between threads. Define the interaction • between threads and between threads and object locks • when executing synchronized, wait, notify, or notifyAll
Threads area java’s way of making a single JVM look like many machines, all running at the same time The CPU switches among the JVM’s various projects to give the impression of multiple CPUs Introduction
Every thread corresponds to an instance of the Thread class Thread fundamentals Java’s thread support resides in The Java.lang.Thread class The Java.lang.Object class The Java language and virtual machine
A thread can be in various states: At any moment, at most one object is executing per CPU, while others might be waiting for resources, or waitingfor a chance to execute, or sleeping, or dead
In order to understand threads, you need to be able to answer these questions • When a thread executes, what code does it execute? • What states can a thread can be in? • How does a thread changes its state?
To make a thread execute, you call its start() method (register the thread with the thread scheduler) This does not immediately cause the thread to run; it just makes it eligible to run. The thread must still contend for CPU time with all the other threads What a thread executes thread scheduler: determines which thread is actually running on each available CPU at any given time
When a thread gets to execute, what does it execute? • The thread can execute its own run() method • The thread can execute the run() method of some other object
public class CounterThread extends Thread { • public void run() { • for (int i=1; i<=10; i++) { • System.out.println(“Counting: ” + i) • } • } • } • CounterThread ct = new CounterThread(); • ct.start(); // start(), not run() Eventually the thread will execute, and at that time its run() method will be called
public Thread(Runnable target) public void run() When you call the thread constructor, you have to specify which object owns the run() method that you want The Runnable interface describes a single method
DownCounter dc = new DownCounter(); • Thread t = new Thread(dc); • t.start(); • public class DownCounter implements Runnable { • public void run() { • for (int i=10; i>=1; i--) { • System.out.println(“Counting: ” + i) • } • } • } Downcounter does not extend Thread, but it has a run() method, and it declares that it implements the Runnable interface
Subclass Thread. • Define your run() method in the subclass • Write a class that implements Runnable. • Define your run() method in that class. • Pass an instance of that class into your call to the • Thread constructor Approaches to specifying which run() method will be executed by a thread
When execution ends When the run() method returns, the thread has finished its task and is considered dead Once a thread is dead, it may not be started again A dead thread continues to exist; you can still access its data and call its methods. You just cannot make it run again!! The thread methods include a method called stop(), which forcibly terminates a thread, putting it into the dead state
If a thread might need to be killed from another thread, you should send it an interrupt() from the killing method!!!
Thread states When you call start() on a thread, it does not run immediatley. It goes into a “ready-to-run” state and stays there until the scheduler moves it to the “running state” • Thread states: • Running the states all threads aspire to • Various waiting states • waiting, sleeping, suspended, blocked • Ready Not waiting for anything except • the CPU • Dead All done
Running Asleep Monitor States Suspended Blocked Ready Living thread states
Thread Priorities Every thread has a priority, an integer from 1 to 10; threads with higher priority get preference over threads with lower priority The priority is considered by the thread scheduler when it decides which ready thread should execute The scheduler generally chooses the highest-priority waiting thread. There is no guarantee that the thread chosen will be the one that has been waiting the longest
int oldPriority=theTread.getPriority(); • int newPriority=Math.min(oldPriority+1, • Thread.MAX_PRIORITY ); • theThread.setPriority( newPriority ); • The default priority is 5, but all newly created threads have their priority set to that of the creating thread • setPriority() method: to set a thread’s priority, passing in the desired priority • getPriority() method: returns a thread’s priority
Controlling threads The art of moving threads from state to state Pathways out of the Running state • Yielding • Suspending and then resuming • Sleeping and then waking up • Blocking and then continuing • the CPU • Waiting and then being notified
Running Ready Yielding A thread can offer to move out of the virtual CPU by yielding A call to yield() method causes the currently executing thread to move to the Ready state yield() scheduled
public void traceRays() { • for (int j=0; j<300; j++) { • for (int i=0; i<300; i++) { • computeOnePixel(i, j) • } • } • } rayTraceThread.setPriority(thread.NORM_PRIORITY-1) Yielding allows a time-consuming thread to permit other threads to execute The ray-tracing thread can have its priority set like this
public void traceRays() { • for (int j=0; j<300; j++) { • for (int i=0; i<300; i++) { • computeOnePixel(i, j) • Thread.yield(); • } • } • }
Suspending allows any arbitrary thread to make another thread un-runnable for an indefinite period of time The suspended thread becomes runnable when some other thread resumes it The effect of suspend and resume is much better implemented using wait and notify
Sleeping A sleeping thread passes time without doing anything and without using the CPU • public static void • sleep(long milliseconds) throws InterruptedException • public static void • sleep(long milliseconds, int nanoseconds) throws • InterruptedException
Running sleep() scheduled Sleeping Time expires or interrupted Ready The Sleeping state A sleep call will block a thread for at least the requested time, but it might block for much longer A sleeping thread that receives an interrupt() call moves immediately into the ready state; when it gets to run, it will execute its interruptedException handler
try { • Socket sock = new Socket(“magnesium”, 5505); • InputStream istr = sock.getInputStream(); • int b = istr.read(); • } • catch (IOException ex) { • // Handle the exception • } Blocking Many methods that perform input or output have to wait for some occurrence in the outside world before they can proceed Example: Reading from a socket
Running Blocking method scheduled Blocked Blocking condition changes or interrupted Ready The Blocked State
Running Sleep Monitor States Suspended Blocked Ready Monitor States
Scheduling Implementations 1. Preemptive scheduling Ways for a thread to leave the running state • It can cease to be ready to execute (e.g, by calling a blocking I/O method) • It can get moved out of the CPU by a higher priority thread that becomes ready to execute 2. Time-sliced or round-robin scheduling A thread is only allowed to execute for a limited amount of time
Monitors, wait(), and notify A monitor is an object that can block and revive threads The reason for having monitors is that sometimes a thread cannot perform its job until an object reaches a certain state
class Mailbox { • public boolean request; • public String message; • } • myMailbox.message = “Hello everybody.”; • myMailbox.request = true; Example: Handling requests to write to standard output A client can set a message to some value, then set a request to true
public class Consumer extends Thread{ • private Mailbox myMailbox; • public Consumer(Mailbox box) { • this.myMailbox = box; • } • public void run() { • while (true) { • if (myMailbox.request) { • System.out.println(myMailbox.message); • myMailbox.request = false; • } • try { • sleep(50); • } • catch (InterruptedException e) { } • } • } • }
Java’s monitor provides the following resources • A lock for each object • The synchronized keyword for accessing an object’s lock • The wait() , notify(), and notifyAll() methods, which allow the object to control client threads
The object lock and synchronization Every object has a lock. At any moment, that lock is controlled by, at most, one single thread. The lock controls access to the object’s synchronized code A thread that wants to execute an object’s synchronized code must first attempt to acquire that object’s lock If the lock is under another thread’s control, then the attempting thread goes into the Seeking Lock state and only becomes ready when the lock becomes available
Running Enter the synchronized code Seeking lock scheduled Lock obtained Ready The Seeking lock State
Ways to mark a code as synchronized Synchronize an entire method by putting the synchronized modifer in the method’s declaration. To execute the method, a thread must acquire the lock of the object that owns the method Synchronize a subset of the a method by surrounding the desired lines of code with curly brackets ({}) and inserting the synchronized(someObject) expression before the opening curly
class Mailbox { • private boolean request; • private String message; • public synchronized void storeMessage(String message) { • request = true; • this.message = message; • } • public synchronized String retrieveMessage() { • request = false; • return message; • } • }
wait() and notify() Provide a way for a shared object to pause a thread when it becomes unavailable to that thread, and to allow the thread to continue when appropriate The threads themselves never have to check the state of the shared object An object that controls its client threads in this manner is known as a monitor Monitor: any object that has some synchronized code
Both wait() and notify() must be called in synchronized code A thread that calls wait releases the virtual CPU; at the same time, it releases the lock. It enters a pool of waiting threads, which is managed by the object whose wait() method got called. Every object has such a pool
myMailbox.retrieveMessage(); • public synchronized String retrieveMessage() { • while (request == false) • try { • wait(); • } catch(InterruptedException e) {} • } • request = false; • return message; • } A message-consuming thread calling this method
Running Waiting Seeking lock Ready The Monitor States Enter the synchronized code wait scheduled notify() or notifyAll() timeout or interrupt Lock obtained
public synchronized void storeMessage(String message) { • this.message = message; • request = true; • notify(); • } Revised storeMessage()
class Mailbox { • private boolean request; • private String message; • public synchronized void storeMessage(String message) { • while(request == true) { // No room for another message • try { • wait(); • } catch (InterruptedException e) {} • } • request = true; • this.message = message; • notify(); • } • public synchronized String retrieveMessage() { • while(request == false) { // No message to retrieve • try { • wait(); • } catch (InterruptedException e) {} • } • request = false; • notify(); • return message; • } • }
Main points about wait() • The calling thread gives up the CPU • The calling thread gives up the lock • The calling thread goes into the monitor’s waiting pool
Main points about notify() • One thread gets moved out of the monitor’s waiting pool and into the Ready • The thread that was notified must re-acquire the monitor’s lock before it can proceed
Beyond the pure model The notify method is not precise: You cannot specify which thread is to be notified A thread might alter the monitor’s state in a way that is useless to the particular thread that gets notified. In such a case, the monitor’s methods should take two precautions • Always check the monitor’s state in a while loop rather than an if statement • After changing the monitor’s state, call notifyAll() rather than notify()
public synchronized void mixedUpMethod() { if ( i < 16 || f > 4.3f || message.equals(“UH-OH”) { try { wait(); } catch (InterruptedException e) { } } // proceed in a way that changes state, and then . . . notify(); } You should not do the following:
public synchronized void mixedUpMethod() { while ( i < 16 || f > 4.3f || message.equals(“UH-OH”) { try { wait(); } catch (InterruptedException e) { } } // proceed in a way that changes state, and then . . . notifyAll(); } The solution is to change mixedUpMethod() as follows
class StrangeSync { Rectangle rect=new Rectangle(11, 13, 1100, 1300 ); void doit() { int x=504; int y=x/3; rect.width -= x; rect.height -= y; } } Strange ways to synchronize • Synchronizing on the lock of a different object • Synchronizing on the lock of a class