270 likes | 589 Views
Thread. All at once …. Multitasking and Multithreading. Multitasking: A computer that is able to perform multiple tasks that appear to occur (and may actually occur) simultaneously. Multithreading : A thread is a single sequence of execution within a program
E N D
Thread All at once …
Multitasking and Multithreading • Multitasking: • A computer that is able to perform multiple tasks that appear to occur (and may actually occur) simultaneously. • Multithreading: • A thread is a single sequence of execution within a program • Refers to multiple threads of control within a single program • Each program can run multiple threads of control
Concurrency vs. Parallelism • Parallelism: A condition that arises when at least two threads are executing simultaneously. • Concurrency: A condition that exists when at least two threads are making progress. A more generalized form of parallelism that can include time-slicing as a form of virtual parallelism.
Concurrency and Parallelism • Concurrent multithreading systems give the appearance of several tasks executing at once, but these tasks are actually split up into chunks that share the processor with chunks from other tasks. • In parallel systems, two tasks are actually performed simultaneously. Parallelism requires a multi-CPU system.
Threads and Processes CPU Process 1 Process 2 Process 3 Process 4 main run GC
What are Threads Good For? • To maintain responsiveness of an application during a long running task • To enable cancellation of separable tasks • Some problems are intrinsically parallel • To monitor the status of some resource (files, databases, networks)
Application Thread • When a Java application is executed • The JVM creates a Thread object whose task is defined by the main() method • The JVM starts the thread • The thread executes the statements of the program one by one • After executing all the statements, the method returns and the thread dies
Multiple Threads in an Application • Each thread has its private run-time stack so that if two threads execute the same method, each will have its own copy of the local variables • However, all threads see the same dynamic memory. In other words, multiple threads may operate on the same object simultaneously. • How does parallelism affect the design of our data structures? • What if two threads simultaneously insert into a tree or map or list?
Two Classes in Java • Runnable interface • defines a single method: public void run() • All Runnable objects can be executed in a thread • Thread class • Represents a thread of execution in a program • Supports numerous methods for manipulating threads • start(): starts execution of the thread • stop(): triggers termination of the thread
Two Techniques • Two ways of creating threads: • Can implement the Runnable interface from which a Thread can be constructed: • Can specialize (subclass) the Thread class. The run method of Thread does nothing. class MyRunnable implements Runnable { public void run() { for(inti=0; i<5; i++){ System.out.println(i); } } } Thread t = new Thread(new MyRunnable()).start(); class MyRunnableThread extends Thread { public void run() { for(inti=0; i<5; i++){ System.out.println(i); } } } Thread t = new MyRunnableThread().start();
Thread States (General) • A Thread may be in • New Thread state: No resources allocated. An empty shell. • Running state: The thread is executing. • Transition from new-state to running-state is via the start method • Not Runnable state (blocked): the thread is waiting • via sleep(int x) • via waiting for a resource • Dead state: thread is over • via the run method terminating
Thread methods • In addition to start the start method, Threads have the following: • void yield(): Causes the currently executing thread object to temporarily pause and allow other threads to execute. • void sleep(int m) or sleep(int m, int n) : The thread sleeps for m milliseconds, plus n nanoseconds
Example public class Printer extends Thread { private String msg; public Printer(String s) { msg = s; } public void run() { while(true) { System.out.println(msg); try { int random = (int)(Math.random()*500); sleep(random); } catch(InterruptedException e) { } } } } public class Drinks { public static void main(String[] args){ Thread t1 = new Printer("I like coffee."); Thread t2 = new Printer("I like tea."); t1.start(); t2.start(); } }
Concurrent Threads • Concurrent: multithreading systems give the appearance of several tasks executing at once, but these tasks are actually split up into “chunks” that share the processor with other tasks • Even if you have a multi-CPU system, this sort of interleaving of processors is almost always guaranteed to happen • Other apps may be running • Your system has all of its own OS-specific processes to manage all the time • All of this requires that the CPU(s) cycle through all the jobs they have to do in some order or other
Case Study • Consider a stack class and the issues that arise when attempting to make the class thread-safe. public interface SimpleStack<V> { public int size(); public booleanisEmpty(); public V pop(); public void push(V value); }
Concurrent Data Structures • Consider a Stack that is managed by multiple threads. Identify areas of concern. public class Stacker extends Thread { private SimpleStack<Double> stack; public Stacker(Stack<Double> s) { this.stack= s; } public void run() { for(int i=0; i<1000; i++){ if(Math.random() < .5) { stack.push(Math.random()*1000); } else if(!stack.isEmpty()) { stack.pop(); } } } } public class Stack<V> implements SimpleStack<V>{ private Node<V> top; public Stack() { top = new Node<V>(); } public boolean add(V value) { top.next = new Node<V>(value, top.next); } public V pop() { V result = top.next.value; top = top.next.next; return result; } public int size() { int count = 0; Node temp = top.next; while(temp != null) { temp = temp.next; count++; } return count; } public booleanisEmpty() { return size() == 0; } } public void makeThreads() { Stack<Double> buffer = new Stack<Double>(); Stacker s1 = new Stacker(buffer); Stacker s2 = new Stacker(buffer); s1.start(); s2.start(); }
Concurrency in Java • Each object in Java is associated with a monitor • A thread can lock or unlock a monitor • Only one thread at a time may hold a lock on a monitor • Any other threads attempting to lock that monitor are blocked until they can obtain a lock on that monitor • A thread may lock a particular monitor multiple times; each unlock reverses the effect of one lock operation • A monitor is associated with a code block which is only executable if a Thread owns the lock (or has locked the monitor)
Java Support for Concurrency • The synchronized keyword associates a monitor with code. • Comes in two flavors: • synchronized code fragments • synchronized methods • Synchronized fragments • synchronized ( Expression ) Block • Expression is a non-null Object • Block is the associated code public class Foo { public void fooBar() { synchronized(someResource) { … some code goes here … } } }
Java Support for Concurrency • Synchronized methods • The synchronized keyword may optionally occur in a method signature • The monitor is the object of which the method is a member • The associated code is the entire method body public class Vector { private Object[] data; private int size; public synchronized void add(Object v) { data[size] = v; size = size + 1; } public synchronized Object removeLast() { Object result = data[size-1]; data[size-1] = null; size = size – 1; return result; } }
Rework the stack • Must ensure that all the stack operations are atomic. • Does synchronization solve all problem(s)? public class ThreadSafeStack<V> implements SimpleStack<V> { private Node top; public ThreadSafeStack() { top = new Node(null, null); } public synchronized int size() { int count = 0; Node temp = top.next; while(temp != null) { count++; temp = temp.next; } return count; } public synchronized booleanisEmpty() { return size == 0; } public synchronized void push(V value) { top.next = new Node(value, top.next); size++; } public synchronized V pop() { if(isEmpty()) throw new IllegalStateException(); V result = top.next.value; top.next = top.next.next; size--; return result; } public class Stacker extends Thread { private SimpleStack<Double> stack; public Stacker(Stack<Double> s) { this.stack= s; } public void run() { for(int i=0; i<1000; i++){ if(Math.random() < .5) { stack.push(Math.random()*1000); } else if(!stack.isEmpty()) { stack.pop(); } } } }
Wait! • Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object. • The current thread must own this object's monitor. • The thread releases ownership of this monitor and waits. • The thread then waits until it can re-obtain ownership of the monitor and resumes execution. • Interrupts and spurious wakeups are possible, and this method should always be used in a loop: • synchronized (obj) { • while (<condition does not hold>) obj.wait(); • // Perform action appropriate to condition • }
Summary • sleep(long millis) - causes the currently executing thread to sleep (temporarily cease execution) for the specified number of milliseconds. • yield() - causes the currently executing thread object to temporarily pause and allow other threads to execute. • wait() - causes current thread to wait for a condition to occur (another thread invokes the notify() method or the notifyAll() method for this object). This is a method of the Object class and must be called from within a synchronized method or block. • notify() - notifies a thread that is waiting for a condition that the condition has occurred. This is a method of the Object class and must be called from within a synchronized method or block. • notifyAll() – like the notify() method, but notifies all the threads that are waiting for a condition that the condition has occurred.
Blocking Stack • We should probably rework the pop method such that when popping the stack, the client blocks if the stack is empty and awakes when an element can be popped. • How might we rework the pop method? public class ThreadSafeStack<V> implements SimpleStack<V> { private Node top; public ThreadSafeStack() { top = new Node(null, null); } public synchronized V pop() { if(isEmpty()) throw new IllegalStateException(); V result = top.next.value; top.next = top.next.next; size--; return result; }
Blocking Stack • Must check to see if isEmpty and then wait if the stack is empty • Must be notified when the stack becomes non-empty. This would be done by some thread that has pushed. • Both the push and pop must be altered in order to support a blocking stack.
Java's Concurrent Structures • The Collections framework defines many types of data structures (Maps, Lists, Sets) each of which has multiple implementations (HashMaps, TreeSets, LinkedLists, etc..) • Some of these are thread-safe: • Vector • HashMap • Many classes are not thread-safe. There are two different ways to construct thread-safe collections. • Use the Collections.sychronizedXYZ(…) methods to convert a non-thread-safe collection into a thread-safe collection • Use one of the classes defined in java.util.concurrent package • ConcurrentHashMap • ConcurrentLinkedQueue • ConcurrentSkipListMap • ConcurrentSkipListSet • CopyOnWriteArrayList • CopyOnWriteArraySet • ArrayBlockingQueue • LinkedBlockingDeque • LinkedBlockingQueue • PriorityBlockingQueue