1 / 73

Topic 13 – Threads and Concurrent Programming

Topic 13 – Threads and Concurrent Programming. CISC370/Object Oriented Programming with Java. “A person with one watch knows what time it is. A person with two watches is never sure.”. Use and Distribution Notice.

Download Presentation

Topic 13 – Threads and Concurrent Programming

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Topic 13 – Threadsand Concurrent Programming CISC370/Object Oriented Programming with Java “A person with one watch knows what time it is. A person with two watches is never sure.”

  2. Use and Distribution Notice • Possession of any of these files implies understanding and agreement to this policy. • The slides are provided for the use of students enrolled in Jeff Six's Object Oriented Programming with Java class (CISC 370) at the University of Delaware. They are the creation of Mr. Six and he reserves all rights as to the slides. These slides are not to be modified or redistributed in any way. All of these slides may only be used by students for the purpose of reviewing the material covered in lecture. Any other use, including but not limited to, the modification of any slides or the sale of any slides or material, in whole or in part, is expressly prohibited. • Most of the material in these slides, including the examples, is derived from multiple textbooks. Credit is hereby given to the authors of these textbook for much of the content. This content is used here for the purpose of presenting this material in CISC 370, which uses, or has used, these textbooks.

  3. Multitasking • Multitasking is very common in modern operating systems; this refers to the ability to have more than one program running at the same time (or making it look like this is happening). • In reality, the operating system is distributing resources (most noticeably CPU time) to create this impression of parallel activity. • Only one program is running at once; the OS simply switches from one to another in very quick switches.

  4. Multithreading • Each “program” that can be multitasked is known as a process. • Multithreaded programs (or concurrent programs) take this idea one step farther; one program can appear to be doing multiple things at one time. • Each task is known as a thread, or a thread of execution. • So, how is a thread different than a process?

  5. Threads vs. Processes • A process runs in its own context, it seems to have its own CPU, memory, registers, and so forth. There is no sharing of data between processes. • A thread also seems to have its own CPU, execution stack, and registers. However, all of the threads in a program share the same variables and data.

  6. Advantages of Multithreading • Multithreading can be extremely useful. • For example, an internet browser can communicate with multiple hosts, open an email editing window, and render images, all at the same time. • Java itself uses multithreading; when a program is running Java is running a garbage-collection thread in the background, to deal with memory management.

  7. Concurrent Programming • A program with more than one thread is known as a concurrent program, which is different than a parallel program. • A parallel program performs more than one task on more than one processor. • A concurrent program performs more than one task, but does so on one processor. In reality, only one thread runs at a time; they simple run for short periods of time. After this short period of time, the thread scheduler chooses another thread to run. (more about scheduling later)

  8. Threads in Java • Any object class can be a thread by extending the Thread class. • A Thread class object must have a run() method which runs when the thread starts and causes the thread to exit when the run() method ends. • For example, let’s create a simple thread class that will simply sleep (wait) for a specified amount of time and then wake up.

  9. class PrintThread extends Thread { public PrintThread(String name) { super(name); sleepTime = (int)Math.random() * 5000; System.out.println(“Name:” + getName() + “; Sleep:” + sleepTime); } public void run() { try { System.out.println(getName() + “ going to sleep.”); Thread.sleep(sleepTime); } catch (InterruptedException exp) { System.out.println(exp); } System.out.println(getName() + “ done sleeping.”); } private int sleepTime; }

  10. Then, we can make a class to use these PrintThread objects… public class ThreadTester { public static void main(String args[]) { PrintThread thr1, thr2, thr3, thr4; thr1 = new PrintThread(“Thread 1”); thr2 = new PrintThread(“Thread 2”); thr3 = new PrintThread(“Thread 3”); thr4 = new PrintThread(“Thread 4”); System.out.println(“Starting Threads...”); thr1.start(); thr2.start(); thr3.start(); thr4.start(); System.out.println(“Threads started.”); } }

  11. Notice that these are random times. Looking at some output from this tester class . . . Name: Thread 1; Sleep: 1446 Name: Thread 2; Sleep: 40 Name: Thread 3; Sleep: 1009 Name: Thread 4; Sleep: 4997 Starting Threads Threads Started Thread 1 going to sleep. Thread 3 going to sleep. Thread 2 going to sleep. Thread 4 going to sleep. Thread 2 done sleeping. Thread 3 done sleeping. Thread 1 done sleeping. Thread 4 done sleeping.

  12. Creating a Thread: Approach 1 • A thread is created by instantiating an object of a class derived from Thread. In this case, this class was PrintThread. • The Thread class constructor (the one we’re using here), takes one parameter, the name of the newly created thread. • This can be any string you want; it exists solely to assist the programmer in keeping track of the threads in the program.

  13. Creating a Thread: Approach 2 • As Java does not support multiple inheritance, and a class that wants to run as a thread must extend the Thread class, this presents a problem. • If a program wants multithreading support in a class that is already derived from another class, this class can implement the Runnable interface instead. • Then, simply add a run() method, just like when the class extended the Thread class.

  14. Creating a Thread: Approach 2 • When this interface approach is used, a Thread object must still be created in order to run the thread. Simply pass the Runnable object to the constructor for the Thread class… • Or, to name the thread object as well… public Thread( Runnable runnableObject ) public Thread( Runnable runnableObject, String threadName )

  15. Creating a Thread: Approach 2 • This creates a new Thread object with the name threadName and registers the run() method of the runnableObject as the code to run when this Thread object’s start() method is called. public Thread( Runnable runnableObject, String threadName )

  16. Thread Lifetime • A thread can be in one of four states at any given moment in time . . . • New • Runnable • Blocked • Dead • We will look at each of these four states in detail.

  17. Thread States: New • When a thread has just been created, by using the new operator (which calls the constructor of the thread class), the thread is in the new state. • In this state, the program has not yet started executing the code inside the thread’s methods. • Once the start() method is called on the thread, it enters the runnable state.

  18. Thread States: Runnable • When a thread is in this state, it is ready to execute. Such a thread may not be actually running on the CPU. It is up to the operating system to schedule the thread. • When this occurs, and the thread is actually running, Java still considers the thread runnable. (Note that in most concurrent programming models there is a distinction between the runnable and running states).

  19. Thread States: Runnable • How the scheduling of different thread is handled is up to the underlying operating system. • Most Java-supporting OSes support preemptive multithreading and time-slicing; this refers to allows one thread to run, then interrupting it to allow another thread to run, then interrupting that to allow another to run, and so forth. • Always remember that a runnable thread may or may not be running at any given moment.

  20. Thread States: Blocked • A thread enters the blocked state whenever: • The thread calls its sleep() method. • The thread calls an operation that blocks on input/output; this is an operation which does not return from the call until the I/O operations are complete. • The thread calls its wait() method.

  21. Thread States: Blocked • The thread tried to lock an object that is currently locked by another thread (more about this in a bit). • A different thread calls the suspend() method of the thread. This method is depreciated and program code should never call it. • When a thread is blocked, another thread is scheduled to run.

  22. Thread States: Blocked • When a thread is reactivated (for example, the blocking I/O operation completes), it returns to the runnable state. • The thread scheduler checks to see if the reactivated thread has a higher priority than the currently running thread. If it does, the scheduler will preempt the currently running thread to schedule to reactivated, higher- priority thread. More about thread priorities in a bit.

  23. Thread States: Blocked • Once a thread has entered the blocked states, it returns to the runnable state using the opposite of the route that is followed to get into the blocked state… • If the thread called sleep(), the specified number of milliseconds to sleep has expired. • If the thread is waiting for blocked I/O, the specified I/O request completed.

  24. Thread States: Blocked • If the thread called wait(), some other thread must call notify() or notifyAll(). More about wait()/notify() later. • If the thread is waiting for an object lock held by another thread, that other thread must have released that lock (again…more in a bit). • There is a one-to-one correspondence between ways to enter the blocked state and ways to return from the blocked state.

  25. Thread States: Dead • A thread can enter the dead state in one of two cases: • Its dies a natural death when the run() method exits normally. • It dies abnormally because the run() method generated an uncaught exception. • To find out if a thread is currently alive, call the isAlive() method. It will return true if the thread is runnable or blocked; false if the thread is new or dead.

  26. Looking at thread states and the possible transitions… sleep() done sleeping blocked new wait() start notify() I/O complete runnable run method exits block on I/O dead

  27. Thread States and Methods • Certain thread methods are only valid when the thread is in certain states. For instance, the start() method can only be called on a thread in the new state and the sleep() method can only be called on a thread in the runnable state. • If a program attempts to calls a method on a thread that is in invalid state, an IllegalThreadStateException is thrown.

  28. Interrupting Threads • A thread dies when its run() method ends. This is the only way a thread can end (actually there is a stop() method which causes the thread to die, but this is deprecated, and should never be used). • So, a thread should periodically test to see if it should terminate… public void run() { while (no die request and still work to do) { do work } // die request or no more work, so end and thread dies }

  29. Interrupting Threads • However, threads frequently sleep or wait, in order to allow other threads a chance to do their work. When a thread is sleeping, it cannot actively check to see if it should terminate (as no code is running). • Java provides an interrupt() method. When this method is called on a thread that is currently blocked, the blocking call (sleep() or wait()) is terminated with an InterruptedException.

  30. Interrupting Threads • Notice that interrupting a thread, using the interrupt() method, does not automatically mean that the thread should terminate. It simply grabs the attention of the thread (kind of like poking a sleeping roommate to wake him up). • In order to accomplish this functionality, place code that will decide how to react to the interruption inside the catch clause of the run() method. • For example…

  31. The main execution loop calls sleep or wait. If the thread is interrupted during one of these calls, the catch clause runs. public void run() { try { // main thread execution loop while (more work to do) { do this work } } catch (InterruptedException exp) // thread was interrupted during sleep/wait { . . . } // exit the run method, so the thread dies }

  32. Interrupting Threads • There is one problem with this code, if the thread is interrupted when it is not sleeping or waiting, no InterruptedException is generation. Thus, the thread must check to make sure it has not been interrupted during the main execution loop. • This is simple by calling the interrupted() method, which returns true if the thread has been interrupted. For example… while (!interrupted() && more work to do) { // main thread execution loop }

  33. I/O and Thread Interruptions • If a thread is blocked waiting for I/O, the input/output operations will not be interrupted by the call to interrupt(). • Therefore, this approach must be used so that when the blocking I/O operation returns the thread will check to see if it has been interrupted.

  34. Waiting for Thread Completion • It is possible to wait for a thread to complete. • To do this, call the thread’s join() method. For instance, to wait for Thread1 to complete call… • A timeout value can also be specified, indicating that if the thread does not die without the specified timeout an InterruptedException is generated. Thread1.join();

  35. Waiting for Thread Completion • For instance, to wait for 500 milliseconds for Thread1 to die, simply specify the timeout value in the call to the join() method… try { Thread1.join(500); System.out.println(“Thread exited.”); } catch (InterruptedException exp1) { System.out.println(“Timeout expired!”); }

  36. Thread Priorities • Each thread has a priority. This is a numerical representation of the relative “importance” of this thread running, compared to the other threads. • A thread’s priority can be set using the setPriority() method. • A thread’s priority can be anywhere between MIN_PRIORITY and MAX_PRIORITY. These are constants, defined as 1 and 10. There is also a NORM_PRIORITY, defined as 5.

  37. Thread Priorities and Scheduling • Whenever the thread scheduler has to schedule a new thread to run, it generally picks the highest-priority thread that is currently runnable. • The highest-priority thread keeps running until either (1) it yields by calling the yield() method, (2) it becomes non-runnable (dying or entering the blocked state), or (3) a higher-priority thread becomes runnable.

  38. Thread Priorities and Scheduling • When one of these three things happens, the scheduler picks a new thread to run by selecting the highest-priority thread among those that are runnable. • What happens if there is more than on runnable thread with the same priority, and that priority is the highest?

  39. Thread Priorities and Scheduling • That is up to the underlying operating system. It will pick which thread to run, but this choice will be made from the runnable threads that have the highest priority (it doesn’t matter which one is chosen, but it has to be one of them). • There is no guarantee this is a fair process! The OS could decide to pick a random choice, or simply pick the first of the highest-priority threads.

  40. Daemon Threads • A daemon thread is a thread that runs solely for the benefit of other threads, such as the Java garbage collector. • These threads are exactly like normal threads with one difference…having a live daemon thread will not stop the JVM from terminating – you need to have at least one non-daemon thread to keep the program alive. • To set a thread to be a daemon thread, call setDaemon() before the start() method is called. To see if a thread is running as a daemon, call isDaemon().

  41. Program Termination and Threads • Whenever at least one thread is alive (runnable or blocked), the program/process the thread(s) belong to is still active. Even if the main() method exits, an alive thread will prevent a program from terminating. • A daemon thread, however, will not prevent a program from terminating.

  42. Thread Groups • In order to simplify working with multithreaded programs, Java introduces the concept of thread groups. • This is a convenient way of grouping related threads. For example, in an internet browser, multiple threads may be loaded different images all to be displayed on the same page. If the user clicks the Stop button, all of these threads should be interrupted. Thus, all of these threads would be placed in the same thread group.

  43. Thread Groups • First, create a new thread group with a name… • Then, when a thread object is constructed, specify its group in the constructor… • To see if any threads in a group are runnable, call activeCount(), which will return an integer number of runnable threads in the thread group… ThreadGroup imgThrs = new ThreadGroup(“ImageThreads”); Thread thr1 = new Thread(imgThrs, “Image Thread 1”); if (imgThrs.activeCount()) > 0

  44. Thread Groups • An entire group of threads can be interrupted at once.. • Thread groups can have child subgroups. A newly created thread group is, by default, a child of the current thread group (but you can change that). Methods such as activeCount() and interrupt() work on all threads in their group, and in all child groups. imgThrs.interrupt(); // interrupts all threads in the // thread group

  45. Thread Synchronization • In most applications where multithreading is used, more than one thread will need to access the same data. • This can turn into a problem when two threads have access to the same object and then both modify the state of the object. • Depending on the order of data access, the object can become corrupted. • This situation is known as a race condition.

  46. Thread Synchronization • Suppose we have a program which is handling transactions for a bank. For each transfer, the program starts a separate thread to perform the transfer. The thread gathers the necessary information and calls the transfer() method of the bank object. • Inside the Bank class there is a transfer() method . . .

  47. This transfer function deducts the transfer amount from the indicated account and adds it to the indicated account. class Bank { . . . public void transfer(int from, int to, int amount) { if (accounts[from] < amount) return; accounts[from] -= amount; accounts[to] += amount; num_transfers++; } . . . int num_transfers; }

  48. Thread Synchronization • Now suppose we have two transfer threads that run at once. Remember that a thread can be interrupted at any time. • Also note that the statement: accounts[from] -= amount; is really composed of three parts, (1) loading accounts[from] into a register, (2) subtracting amount from it, and (3) saving the result back into memory.

  49. Thread Synchronization • So, the program has two transfer threads running and they both call the transfer function of the Bank object, each of which will execute these three steps. Where is the potential problem? Thread 1: LOAD ACCOUNTS[from], R1 ADD R1, amount SAVE R1, ACCOUNTS[from] Thread 2: LOAD ACCOUNTS[from], R1 ADD R1, amount SAVE R1, ACCOUNTS[from]

  50. Thread Synchronization • Since a thread can be interrupted at any time for another thread to run, this order of execution is possible… Thread 1 gets preempted just before it would have written its new data back. Thread 2 runs and updates this value. Then thread 1 runs again and overwrites thread 2’s changes! THR1: LOAD ACCOUNTS[from], R1 THR1: ADD R1, amount THR2: LOAD ACCOUNTS[from], R1 THR2: ADD R1, amount THR2: SAVE R1, ACCOUNTS[from] THR1: SAVE R1, ACCOUNTS[from]

More Related