Tirgul 8 Concurrency cont 1 Atomic Variables Cont
Tirgul 8 Concurrency (cont…)
1. Atomic Variables (Cont…) 2. Singleton 3. Callables • Callable • Future • Executor. Service 4. Thread Cancellation
Atomic variables
Atomic Library
CAS – Compare And Swap* A compare-and-swap (CAS) is an cpu atomic instruction that behaves as follows: boolean cas(int target, int old. Value, int new. Value) { // target – A reference to a place in memory // old. Value – The expected value at target // new. Value – A new value that will be set // to target if target equals old. Value // The instruction return true if a set was performed if (target == old. Value) { target = new. Value; return true; } return false; } CAS does not make use of locking (synchronized) rather it is very optimistic in nature Atomic library uses CAS (Atomic. Integer, Atomic. Boolean, Atomic. Integer. Array, Atomic. Reference and more…) * Some are calling it “Compare And Set”
synchronized methods Using atomic library public class Synchronized. Counter { private int c = 0; public synchronized void increment() { c++; } public synchronized void decrement() { c--; } public synchronized int value() { return c; }} class Atomic. Counter{ private Atomic. Integer c = new Atomic. Integer(0); public void increment(){ c. increment. And. Get(); } public void decrement(){ c. decrement. And. Get(); } public int value(){ return c. get(); }} class Synchronized. Even { private long n = 0; class Atomic. Even { private Atomic. Integer n = new Atomic. Integer(0); public synchronized long inc. By 2() { n++; return n; } public long inc. By 2() { int v int do { v = n. get(); } while (!n. compare. And. Set(v, v+2)); return v; } Atomic variable - Example
public class Linked. List<T> { private Link<T> head = null; public synchronized void add(T data) { head = new Link<>(head, data); } public static void main(String[] arg) { Linked. List<Integer> list = new Linked. List<>(); new Thread(()->list. add(1)). start(); new Thread(()->list. add(2)). start(); new Thread(()->list. add(3)). start(); }} * Not necessarily this order // A lock-free implementation private Atomic. Reference<Link<T>> head = new Atomic. Reference(null); public void add(T data) { 1 Link<T> temp. Head = head. get(); 2 Link<T> new. Link = new Link<>(temp. Head, data); 3 1 2 while (!head. compare. And. Set(temp. Head, new. Link)) { boolean cas(int target, int old. Value, int new. Value) { temp. Head = head. get(); if (target == old. Value) { new. Link. next = temp. Head; target = new. Value; return true; head old. Head new. Head } } } public void main() {…}} Basic Idea: 1. Copy the current CAS target to a local variable 2. Create/update a/the new value as a local variable 3. Retry to commit the change using cas instruction until succeed } return false; (=temp. Head) (=new. Link) Q: Is this thread safe? 1. Yes, it uses CAS instruction for set 2. No, if the thread is interrupted here another thread can change the head 3. No, the head might not be as expected by the end of the method Atomic variable - Example
public void add(T data) { Link<T> temp. Head = head. get(); Initial state: Link<T> new. Head = new Link<>(temp. Head, data); while (!head. compare. And. Set(temp. Head, new. Head)) { temp. Head = head. get(); new. Head. next = temp. Head; } temp. Head new. Head } 2 options: CAS Successed: CAS Successed after N attempts: head=? CAS Failed:
PROS • There is no usage of synchronized anywhere in the class • Assuming threads t 1, . . . , tn that attempt to invoke add a single time, all at once, then without the loss of generality: t 1 will loop once, t 2 at most twice, and tn at most n times • This code run significantly faster than the synchronized one as we do not send a thread to sleep. CONS • Lock-free classes are known for being much harder to write correctly Exp • There are cases where a lock-free implementation may lead to poor performance. For example when a large amount of data is copied to a local variables. • Lock-free classes cannot block threads, i. e. call wait(), which may be required as seen in blocking queue. (Q: Way not ? ) Because wait() can be called only within a synchronized block There is no silver bullet !! One needs to consider many different techniques and use the most suitable Atomic variable – Pros & Cons ?
Singleton
The problematic implementation Solution 1: Create at load time Solution 2: synchronized method public class Classic. Singleton { private static Classic. Singleton instance=null; private static Classic. Singleton instance = new Classic. Singleton(); private Classic. Singleton() {. . . } private static Classic. Singleton instance=null; public static Classic. Singleton get. Instance() { return instance; } public static synchronized Classic. Singleton get. Instance() { if (instance == null) { instance = new Classic. Singleton(); } return instance; }} Because only one thread is responsible for loading classes in java, this implementation will be thread safe with only one instance. Because only one thread at a time can be within the get. Instance() method whenever the Classic. Singleton class will be imported, the instance will be created even if not needed. Creates a bottleneck if a lot of threads use the singleton. Synchronized means blocking every thread that wants to get the singleton if another thread is currently getting it. private Classic. Singleton() {. . . } } public static Classic. Singleton get. Instance() { if (instance == null) { instance = new Classic. Singleton(); } return instance; } If 2 threads get to this point simultaneously two distinct singleton instances will be created. } Singleton private Classic. Singleton() {. . . } Synchronization problems ?
Solution 3: Double checking Solution 4: Use a static wrapper public class Classic. Singleton { private static Classic. Singleton instance=null; private static class Singleton. Holder { private static Classic. Singleton instance = new Classic. Singleton(); } private Classic. Singleton() {. . . } public static Classic. Singleton get. Instance() { if (instance == null) { synchronized (Classic. Singleton. class) { if (instance == null) instance = new Classic. Singleton(); } } return instance; }} new is not an atomic action it consist of two actions: 1. Allocating memory 2. Calling its constructor. If the thread was preempted after the first action, the memory was already allocated but not constructed. If another thread calls get. Instance(), an uninitialized object will be returned. ? private Classic. Singleton() {. . . } } public static Classic. Singleton get. Instance() { return Singleton. Holder. instance; } ? Singleton • The Singleton. Holder class won't be loaded until we reference it. • When it imports the Classic. Singleton class, the instance won't be created until get. Instance() method is called (Only then the Singleton. Holder is referenced). • Because only a single thread loads classes, only one instance will be created. Synchronization problems
Callables • Callable • Future • Executor. Service
• Callable is an interface that: 1. Designed for tasks that are potentially executed by other threads. 2. Defines a single method with no arguments called call() 3. Enables a thread to return a computed result 4. Uses generic to define the returned type 5. May throw an exception when unable to compute a result Runnable Interface: package java. lang; Callable Interface: package java. util. concurrent; public interface Runnable { public abstract void run(); } public interface Callable<V> { V call() throws Exception; } Q: Who gets the returned value? How? Callables - Callable 1 -2 Same as Runnable 3 -5 discrete from Runnable
• Future is an interface that represents the returned value of an asynchronous task public interface Future<V> { V get() throws Interrupted. Exception, Execution. Exception; // Returns the computed result. Waits for the computation to complete. // Throws exception if the computation: // 1) was cancelled 2) Threw an exception 3) If the thread was interrupted V get(long timeout, Time. Unit unit) throws Interrupted. Exception, Execution. Exception, Timeout. Exception; // Returns the computed result. // Waits at most the given time, and then retrieves its result, if available. // Throws exception same as get() and also Timeout. Exception if the wait timed out boolean is. Done(); // Returns true if this task completed (normal termination, an exception, or cancellation) boolean cancel(boolean may. Interrupt. If. Running); // Attempts to cancel execution of this task. } boolean is. Cancelled(); //Returns true if this task was cancelled before it completed normally Q: OK, But who create the Future? When? Callables - Future
• The executor’s submit() method submits a Callable<V> and return a Future<V> public interface Executor. Service extends Executor { <T> Future<T> submit(Callable<T> task); // Submits a value-returning task for execution and returns a // Future representing the pending results of the task. The // Future's get() method will return the task's result upon // successful completion. <T> Future<T> submit(Runnable task, T result); // Submits a Runnable task for execution and returns a Future // representing that task. The Future's get() method will // return the given result upon successful completion. Future<? > submit(Runnable task); // Submits a Runnable task for execution and returns a Future // representing that task. The Future's get() method will // return null upon successful completion. <T> List<Future<T>> invoke. All(Collection<? extends Callable<T>> tasks) // Executes the given tasks, returning a list of throws Interrupted. Exception; // Futures holding their status and results // when all complete. void execute(Runnable task); // Executes the given Runnable (…The old way, as we learned before). void shutdown(); } //. . . And many more methods Callables - Executor. Service
Runnable Executor. Service execute() Callable<V> Executor. Service submit() Future<V> Callables vs. Execute
• An example that combines all three interfaces – Callable, Future, Executor. Service Run a thread using execute: (From tigul 6) Runnable Executor. Service execute() Run a thread using submit: Callable<V> Executor. Service submit() Future<V> import java. util. concurrent. *; class Simple. Task implements Runnable { public void run() {…} } import java. util. concurrent. *; { class Simple. Task implements Callable<Integer> Runnable { public void run() {…}{…} Integer call() } public class Ex 2 { public static void main(String[] a) { // Create an executor: Executor. Service e = Executors. new. Fixed. Thread. Pool(3); List<Future<Integer>> list = new Array. List<>(); // create several threads. for(int i=0; i<10; i++) { Simple. Task r = new Simple. Task(i); e. execute(r); list. add(e. submit(r); )); } <T> Future<T> submit(Callable<T> task); e. shutdown(); for (Future<Integer> f : list) if (f. is. Done()) …. }} // create several threads. for(int i=0; i<10; i++) { Simple. Task r = new Simple. Task(i); e. execute(r); } void execute(Runnable task); e. shutdown(); }} ** See example in Practical. Session 08 Callables - Example
Run a thread using submit: Callable<V> Executor. Service submit() Future<V> import java. util. concurrent. *; class Simple. Task implements Callable<Integer> { public Integer call() {…} } public class Ex 2 { public static void main(String[] a) { // Create an executor: Executor. Service e = Executors. new. Fixed. Thread. Pool(3); List<Future<Integer>> list = new Array. List<>(); // create several threads. for(int i=0; i<10; i++) { Simple. Task r = new Simple. Task(i); list. add(e. submit(r)); } e. shutdown(); for (Future<Integer> f : list) if (f. is. Done()) …. }} Q: What to do if a thread is not done? Callables - Problem
Run a thread using submit: Callable<V> Executor. Service submit() Future<V> import java. util. concurrent. *; class Simple. Task implements Callable<Integer> { public Integer call() {…} } public class Ex 2 { public static void main(String[] a) { // Create an executor: Executor. Service e = Executors. new. Fixed. Thread. Pool(3); List<Future<Integer>> list = new Array. List<>(); // create several threads. for(int i=0; i<10; i++) { Simple. Task r = new Simple. Task(i); list. add(e. submit(r)); } e. shutdown(); for (Future<Integer> f : list) try { Integer i = f. get(); } catch (Interrupted. Exception | Execution. Exception ex) {} }} Waste a lot of time waiting for one Future to finish while there are other Futures already finished Callables - Problem
• The class Executor. Completion. Service<V>: • Places in a queue the Futures of submitted tasks upon Callable completion • Uses a supplied Executor to execute tasks • The Futures are accessible using take() that waits if none are yet present – very convenient!! public class Executor. Completion. Service<T> { public Executor. Completion. Service(Executor executor) {…} public Future<T> submit(Callable<T> task) {. . . } public Future<T> submit(Runnable task, T result) {. . . } } public Future<V> take() throws Interrupted. Exception { return completion. Queue. take(); what is completion. Queue? } Blocking. Queue … ** See example in Practical. Session 08 Callables - Executor. Completion. Service
public class Simple. Task implements Callable<Integer> {. . . public Integer call() { for (int i = 0; i < 4; i++) System. out. print(" " + num); return num; } Executor. Service<V> vs Executor. Completion. Service public static void main(String[] args) throws Interrupted. Exception, Execution. Exception { } public static void main(String[] args) throws Interrupted. Exception, Execution. Exception { Executor. Service e = Executors. new. Fixed. Thread. Pool(3); List<Future<Integer>> list = new Array. List<Future<Integer>>(); Executor. Service e = Executors. new. Fixed. Thread. Pool(3); Completion. Service<Integer> ecs = new Executor. Completion. Service<>(e); for(int i=0; i<10; i++) { // create & submit several callable Simple. Task r = new Simple. Task(i); list. add(e. submit(r)); } e. shutdown(); for(int i=0; i<10; i++) { // create & submit several callable Simple. Task t = new Simple. Task(i); ecs. submit(t); } e. shutdown(); for (Future<Integer> f : list) { System. out. print(f. is. Done() ? "n. Task " + f. get() + " is done" : "n. Task ? ? ? not done"); } for (int i=0; i < 10; ++i) { Integer res = ecs. take(). get(); // Waits if none are yet present System. out. println(“ Task-" + res + " is done"); } System. out. println(“ all done!"); } Callables - Executor. Completion. Service
public class Simple. Task implements Callable<Integer> {. . . public Integer call() { for (int i = 0; i < 4; i++) System. out. print(" " + num); return num; } Executor. Service<V> vs Executor. Completion. Service public static void main(String[] args) throws Interrupted. Exception, Execution. Exception { Executor. Service e = Executors. new. Fixed. Thread. Pool(3); The output: List<Future<Integer>> list = new Array. List<Future<Integer>>(); Executor. Service e = Executors. new. Fixed. Thread. Pool(3); The output: Completion. Service<Integer> ecs = new Executor. Completion. Service<Integer>(e); 000033334444 Task 0 is done 5 5 6 6 7 7 8 8 9 9 for(int i=0; i<10; i++) { // create & submit several callable Task ? ? ? Is not. Simple. Task(i); done Simple. Task r = new Task ? ? ? Is not done list. add(e. submit(r)); Task 3 is done } Task 4 is done e. shutdown(); Task 5 is done Task 6 is done for (Future<Integer> f : list) { System. out. print(f. is. Done() ? "n. Task " + f. get() + " is done" : Task 7 is done "n. Task ? ? ? not done"); Task 8 is done } Task 9 is done 2 2 1 1 } } 000222203333444466 Task-2 is done for(int i=0; i<10; i++) { // create & submit several callable Task-0 tis= done Simple. Task new Simple. Task(i); Task-3 is done ecs. submit(t); Task-4 is done 5 6 6 1 } Task-6 is done 5 5 5 1 7 1 8 e. shutdown(); Task-5 is done 8 1 7 9 for (int i=0; i < 10; ++i) {8 9 7 9 8 9 Task-1 is done Integer. Task-8 res = is ecs. take(). get(); // Waits if none are yet present done System. out. println(“ Task-9 is done 7 Task-" + res + " is done"); } Task-7 is done System. out. println(“ all done!"); all done! Callables - Executor. Completion. Service
Thread Cancellation
Thrtee ways to stop a working thread: 1. Calling thread’s stop() method - This is always unsafe, and should never be used “Thread. stop causes it to unlock all of the monitors that it has locked. If any of the objects previously protected by these monitors were in an inconsistent state, the damaged objects become visible to other threads, potentially resulting in arbitrary behavior. ” (java doc) 2. Telling the thread it should stop, by changing a variable thread sees. “The target thread should check this variable regularly, and return from its run method in an orderly fashion if the variable indicates that it is to stop running” (java doc) 3. By using the interrupt mechanism “If the target thread waits for long periods, the interrupt() method should be used to interrupt the wait. ” (java doc) Thread Cancellation
• An example for thread cancellation using a variable public lass Simple. Task implements Callable<Integer> { private int num; private boolean should. Stop = false; public Simple. Task(int i) { this. num = i; } //Constructor public synchronized void stop() { //Called by another thread should. Stop = true; } public synchronized boolean is. Should. Stop() { return should. Stop; } public Integer call() { for (int i = 0; i < 4 && !is. Should. Stop(); i++) { System. out. print(" " + num); } return num; }} Q: What is the problem with this approach? The thread might be in a wait() and it doesn’t see the variable Q: Why calling is. Should. Stop() and not accessing should. Stop directly? because the call to stop() happens from another thread. In order to guarantee that the current thread sees the change it must access should. Stop under synchronization Thread Cancellation
• Thread interrupt mechanism Each thread has a flag specifying if it is in interrupted state. An interrupted state means that another thread signals that it should stop is. Interrupted() - Checks if the thread is in interrupted state. interrupt() - If the thread is blocked (wait, sleep or join) an Interrupted. Exception is thrown and the flag is clear, in order to set it one needs to call interrupt() again (very important) - If the thread is not blocked the flag is set to true • If using an Executor to run the threads, calling shutdown. Now() will call interrupt() on all of the threads in the Executor. • • Thread Cancellation - Interrupt
public class Simple. Task. Interrupt implements Runnable { private int num; public Simple. Task. Interrupte(int i) { this. num = i; } public void run() { while (!Thread. current. Thread(). is. Interrupted()) { try { for (int i = 0; i < 4 && !Thread. current. Thread(). is. Interrupted(); i++) System. out. print(" " + num); System. out. println(""); //New line synchronized(this) { this. wait(100); } } catch (Interrupted. Exception e) { // raise the interrupt. This is very important! Thread. current. Thread(). interrupt(); } } System. out. println(" stopping"); } } public static void main(String[] args) { Thread t = new Thread(new Simple. Task. Interrupt(100)); t. start(); try { Thread. sleep(450); } catch (Interrupted. Exception e) { } t. interrupt(); } The output: 100 100 100 100 100 stopping Q: What will happen if we remove this line? Thread will never stop if it is blocked Q: Why we need this condition if we are using try-catch? No exception will be raised when thread is not blocked Q: Why we need this synchronized block? wait() needs a monitor, therefore can be called only within a synchronized block Thread Cancellation – Interrupt example
THE END !! Pease read Practical Session 08 for more example
- Slides: 29