Tirgul 6 Threads Callbacks Threads OS can perform
Tirgul 6 Threads Callbacks
Threads
OS can perform several programs simultaneously For not related tasks - Using a different process is good For related tasks (shared resources) – Using same process might be good Using same process is done by using Threads Several Threads can run simultaneously on a single process and share resources (but not their stack) • Special care should be taken when using Threads. • Task - Something you want done • Thread – Excecutes/Runs the task In Hebrew תהליכון • • • Introduction to threads
• Step 1: Define the tasks by implementing the Runnable interface • Step 2: Create threads and assign each with a task (2 ways) Step 1: Interface: package java. lang; Runnable implementation: class Simple. Task implements Runnable { // If an object that implements Runnable // is used to create a thread, starting // the thread causes the object's run method to // be called in that separately executing thread. public interface Runnable { public abstract void run(); } // ** The 'public abstract' declaration is redundant, since all // symbols are public and abstract by default in an interface private int num; public Simple. Task(int i) { this. num = i; } public void run() { for (int i = 0; i < 4; i++) { System. out. print(" " + num); } }} Using Threads 2 Steps
• Step 2: Create threads and assign each with a task (first way) Run a thread using Thread: class Simple. Task implements Runnable {. . . } public class Ex 1 { public static void main(String[] a) { // create several runnables, and execute them. for (int i=0; i<10; i++) { Runnable r = new Simple. Task(i); Thread t = new Thread(r); t. start(); } } } • start() causes the thread t to begin execution • The Java Virtual Machine calls the run method of the attached runnable. • The result is that 11 threads are running concurrently: 1. The current thread which performs the call to the start() method 2. The new 10 threads which execute their run method. • It is never legal to start a thread more than once. • In particular, a thread may not be restarted once it has completed execution Using Threads 2 Steps
• Step 2: Create threads and assign each with a task (Second way) Run a thread using Executors: class Simple. Task implements Runnable {. . . } import java. util. concurrent. *; public class Ex 2 { public static void main(String[] a) { // Create an executor: Executor. Service e = Executors. new. Fixed. Thread. Pool(3); // create several runnables, and execute them. for(int i=0; i<10; i++) { Simple. Task r = new Simple. Task(i); e. execute(r); } e. shutdown(); // Causes the executor not to accept more tasks, // and to kill the threads when the submitted } // tasks are done. } Executors. new. Fixed. Thread. Pool(3): • Creates a pool of threads operating on a shared queue • At any point, at most n. Threads(3) threads have active tasks. • If additional tasks are submitted when all threads are active, they will wait in the queue until a thread is available. • If any thread terminates due to a failure prior to shutdown, a new one will take its place • The threads in the pool will exist until it is explicitly shutdown Using Threads 2 Steps
import java. util. concurrent. *; Q: How many tasks? 10 Q: How many threads? 3+1 Q: How many numbers will be printed? 40 (4 x 10) Q: What will be printed for a pool of 3 threads? class Simple. Task implements Runnable { private int num; public Simple. Task(int i) { this. num = i; } public void run() { for (int i = 0; i < 4; i++) System. out. print(" " + num); }} class Ex 2 { public static void main(String[] a) { Executor. Service e = Executors. new. Fixed. Thread. Pool(3); for(int i=0; i<10; i++) { Simple. Task r = new Simple. Task(i); e. execute(r); } e. shutdown(); }} Output run 1: 0121101203233344445555666607278797888999 Output run 2: 1111333344445555666677707008089898992222 Output run 3: 00002222341314535456578687969799 Q: What will be printed for a pool of 100 threads? Output run 1: 101044447777222233335658556888669999 Output run 2: 0032333002225545544466699996777788881111 Using Threads
Safety problems arise when multiple threads access a shared resource A shared resources - any object that is visible to several threads • Global (static) objects • Non-primitive method parameters (Since they are passed by-reference) • Class members Thread A Thread B Not a shared resource • Local function variables (Each thread has its own stack) Synchronization problems sharing resources Shared memory
class Even { private int n = 0; // n is even public int inc. By 2() { ++n; //Use sleep to make the problem occur more frequently try {Thread. sleep(30); } catch (Interrupted. Exception e) {} return ++n; }} class Even. Task implements Runnable { Even even; Even. Task(Even e) { even = e; } public void run() { for (int i=0; i<3; i++) System. out. println(even. inc. By 2()); }} public class Ex 3 { public static void main(String[] args) { Executor. Service e = Executors. new. Fixed. Thread. Pool(10); Even even = new Even(); for (int i=0; i<3; i++) e. execute(new Even. Task(even)); //All tasks share the same Even object e. shutdown(); }} Q: What will be printed? 1. Odd numbers 2. Even Numbers 3. Both 4. Neither Q: The Subsequence is 1. monotonically increasing 2. monotonically decreasing 3. no monotonic Synchronization problems sharing resources The output is : 4 6 5 10 11 11 15 17 16
The output was: class Even { … 4 public int inc. By 2() { 6 ++n; 5 … Thread. sleep(30); … 10 return ++n; 11 … }} 11 class Even. Task implements Runnable { … 15 public void run() { 17 for (int i=0; i<3; i++) 16 System. out. println(even. inc. By 2()); Reminder from previous example: Q: What is the highest expected number of the output? 18 (3 (tasks) X 3 (loop) X 2 (inc)) Q: Why We received 17, Why it decreases? n++ is not an atomic command it Involves three basic operations: 1. Set a temporary “variable” with the value of n 2. Increment that value by 1 3. Assign the new value to n … }} public class Ex 3 { public static void main(String[] args) { … Even even = new Even(); for (int i=0; i<3; i++) e. execute(new Even. Task(even)); …}} Synchronization problems(Cont…) sharing resources
• Singleton - having exactly one instance of a class in the program The classic Singleton (naive implementation): public class Classic. Singleton { private static Classic. Singleton instance = null; private Classic. Singleton() { // initialization code. . } public static Classic. Singleton get. Instance() { if (instance == null) { instance = new Classic. Singleton(); } return instance; }} Q: Will it work for single/multi thread? Yes for single thread. No for multi-threads If 2 threads get to this point simultaneously two distinct singleton instances will be created. Synchronization problems sharing resources
Design Approaches towards Avoiding Safety Problems 1. Thread Confinement 2. Immutability Design Approaches on using Threads
1. Thread Confinement – When a variable/resource is accessed only by one thread 1. 2. There's no language or JVM-level mechanism that confines an object to a single thread. The programmer has to ensure that the object can't be accessed by another thread. class Stuff { Object o; Stuff(Object o) {this. o = o; } public void modify() {. . . } } class Confined. By. Stack { void safe. Foo() { //Called by multiple threads Stuff my. Stuff = new Stuff("I'm safe"); my. Stuff. modify(); } void unsafe. Foo(Object o) { //Called by multiple threads Stuff my. Stuff = new Stuff(o); my. Stuff. modify(); }} class Confined. De. Facto { private static final Map<Thread, Stuff> map = new Hash. Map<>(); void foo() { //Called by multiple threads Stuff stuff = map. get(Thread. current. Thread()); if (stuff == null) { stuff = new Stuff(); map. put(Thread. current. Thread(), stuff); } stuff. modify(); }} • everything in the safe. Foo() is confined The method is safe to use in a multi-threaded situation • In unsafe. Foo() Object o is not thread confined (We don't know anything about o, and who else can access it beside current thread) The method is not safe to use in a multi-threaded situation Map is not confined to a thread Stuff stuff object is not confined to a thread. It is the implementation that confined stuff to a thread by using runtime conditions such that the objects is, de facto, never shared between different threads. ** Assuming Hash. Map is thread safe object Design Approaches on using Threads
2. Immutability – When object's state cannot be changed after construction. A class will be immutable if all of the following are true: 1. All fields are final and private. 2. The class is declared final (Subclasses can’t override methods) 3. The object has been safely constructed (reference to “this” hasn't escaped during construction and no thread have accessed the object before the construction completed). 4. Any field that contain reference(s) to mutable object(s) such as list, array, etc… • • • Are private Are never exposed nor returned to callers Are the only reference to the object that it references Doesn’t have "setter" methods Do not change the referenced objects after construction Design Approaches on using Threads
public final class Cow { final class Cowboy {. . . } Q: Is Cow mutable or immutable object? private final int id; // private final String name; // private final Date DOB; // private final Cowboy handler; // Final primitive data is always immutable. String is an immutable object by definition. ! A mutable object field, This field shouldn’t be changed after construction public Cow (int _id, String _name, Date _DOB, Cowboy _handler ) { id = _id; // Set a copy of a primitive value name = _name; // Set a copy of an immutable type handler = _handler; // Mutable, sensitive to the changes that the caller may make to the original handler object DOB = new Date(_DOB. get. Time()); // Immutable, make a private copy of _DOB, this is the only way } // to keep DOB private and protected from outside changes public int get. Id() { return id; // Returns a copy of a primitive value. Caller changes wont affect this. id } public String get. Name() { return name; // Returns an immutable object, String is immutable and cannot be changed } public Cowboy get. Handler() { return handler; // The caller gets a direct reference to a mutable field of “this”, exposes handler to outside world } public Date get. DOB() { return new Date(DOB. get. Time()); // Returned a copy of date object, without affecting the internals of this class } } // Note that Date. get. Time() returns primitive long Immutability Cont…
Callbacks
• A callback is a function that is passed to another function as a parameter, and can be activated from there • In C, a function pointer is passed as parameter to another function and can be invoked (that is, "called back") as needed. foo() { g(&f); } g(T (*_f)(void)) { _f(…); } T f(…) { … } foo() calls g() with the address of f(), g() invokes f() Call back • Java's interfaces provide a mechanism by which we can get the equivalent of callbacks Callback
interface Num. Factory { long generate(); } class Even. Num implements Num. Factory { public long generate() { return Callback. Ex 1. rand. next. Long(100)*2; }} public class Callback. Ex 1 { public static Random rand; public static void print. Numbers(Num. Factory nf) { //nf parameter holds the function generate to be called System. out. println(nf. generate() + " & " + nf. generate()); } class Odd. Num implements Num. Factory { public long generate() { return Callback. Ex 1. rand. next. Long(100)*2+1; }} public static void main(String[] args) { rand = new Random(); rand. set. Seed((new Date()). get. Time()); class Meaning. Of. Life implements Num. Factory { public long generate() { return 42; }} //Passing a Num. Factory object simulates passing a function address in C print. Numbers(new Even. Num()); // 44 & 50 (any 2 even numbers 0 -198) print. Numbers(new Odd. Num()); // 163 & 9 (any 2 odd numbers 1 -199) print. Numbers(new Meaning. Of. Life()); // 42 & 42 }} Callback
Same as previous example with Lambda expressions instead of classes public class Callback. Ex 2 { interface Num. Factory { long generate(); } class Even. Num implements Num. Factory { public long generate() { return Callback. Ex 1. rand. next. Long(100)*2; }} y r a s s class Odd. Num implements Num. Factory { public long generate() { return Callback. Ex 1. rand. next. Long(100)*2+1; }} u n n e class Meaning. Of. Life implements Num. Factory { public long generate() { return 42; }} public static void print. Numbers(Num. Factory nf) { //nf parameter holds the function generate to be called System. out. println(nf. generate() + " & " + nf. generate()); } public static void main(String[] args) { Random rand = new Random(); rand. set. Seed((new Date()). get. Time()); //Passing a Num. Factory object simulates passing a function address in C print. Numbers(()->rand. next. Long(100)*2); 34 & 78 (2 even numbers 0 -198) print. Numbers(()->rand. next. Long(100)*2+1); 13 & 69 (2 odd numbers 1 -199) print. Numbers(()->42); 42 & 42 }} Callback
Void on. Mouse. Over() { make. Sound(Cash. Register); } A Mouse manager object (listener) subscribe. For. Click (Clickable f) { click. Event. List. add(f); } subscribe. For. Mouse. Over (Overable f) { over. Event. List. add(f); } Btn 3 Btn 2 (Clickable) void on. Click() { spin(); } Btn 3 (Clickable, Overable) void on. Click() { make. Sound(Drum); } Void on. Mouse. Over() { make. Sound(lazer); } Over. Event. List click. Event. List btn 1 over. Callback btn 3 over. Callback btn 1 click. Callback btn 2 click. Callback Btn 3 click. Callback Subscribe e d Btn 1 o c o d Btn 1 (Clickable, Overable) u void on. Click() { e s teeter(); P } call. Subscribers. For. Click () { Invokes click. Event. List callbacks } call. Subscribers. For. Over () { Invokes over. Event. List callbacks } interface Clickable { void on. Click(); } interface Overable { void on. Mouse. Over(); }
public interface Deadline. Event { void callback(); } public class Deadline. Notifier implements Runnable { public static void main(String[] args) { private List<Deadline. Event> subscribers = new Linked. List<Deadline. Event>(); for (int i=0; i<3; i++) { Cow cow = new Cow(i); //This lambda implements the callback method of Deadline. Event interface Deadline. Event dle = () -> System. out. println( "{Cow "+ cow. get. Id() +" is notified, It is time to eat. n" + "This callback function was DEFINED in Thread: " + thread. ID + "n" + " ACTIVETED in Thread: " + Thread. current. Thread(). get. Id() +"}"); notifier. subscribe(dle); } {Cow 0 it is time to eat. This callback function was DEFINED in Thread: 1 Thread t = new Thread(notifier); ACTIVETED in Thread: 13} t. start(); {Cow 1 it is time to eat. } This callback function was DEFINED in Thread: 1 ACTIVETED in Thread: 13} {Cow 2 it is time to eat. This callback function was DEFINED in Thread: 1 ACTIVETED in Thread: 13} public void subscribe(Deadline. Event dle) { subscribers. add(dle); } public void run() { try { Thread. sleep(3000); } catch (Exception e 1) {} for (Deadline. Event e : subscribers) { e. callback(); } } Q: How many threads? 2 (main thread + the notifier) Deadline. Notifier notifier = new Deadline. Notifier(); long thread. ID = Thread. current. Thread(). get. Id(); Q: What will be printed? (more or less ) Callback
interface Noisy { public void noise(); } public class Ex 5 { static String sound = "Boo"; static String get. Sound() { return sound; } static void set. Sound(String s) { sound = s; } static void make. Noise(Noisy n) { n. noise(); } public static void main(String[] a) { String current. Sound = get. Sound(); void noise () { System. out. println(current. Sound + " & " + get. Sound()); } Noisy noisy = () -> System. out. println(current. Sound + " & " + get. Sound()); make. Noise(noisy); // Boo & Boo set. Sound("Moo"); current. Sound = get. Sound(); // local variables referenced from a lambda // expression must be final or effectively // final** make. Noise(noisy); // Boo & Moo } } ** See more details in Practical. Session 06 Back to Lambda (because we need it)
THE END !! please read the “JJM” section Practical Session 06
public interface Deadline. Event { void callback(); } public class Deadline. Notifier implements Runnable { public static void main(String[] args) { private List<Deadline. Event> subscribers = new Linked. List<Deadline. Event>(); for (int i=0; i<3; i++) { Cow cow = new Cow(i); //This lambda implements the callback method of Deadline. Event interface Deadline. Event dle = () -> System. out. println( "{Cow "+ cow. get. Id() +" is notified, It is time to eat. n" + "This callback function was DEFINED in Thread: " + thread. ID + "n" + " ACTIVETED in Thread: " + Thread. current. Thread(). get. Id() +"}"); notifier. subscribe(dle); {Cow 0 it is time to eat. } This callback function was DEFINED in Thread: 1 Thread t = new Thread(notifier); ACTIVETED in Thread: 13} t. start(); {Cow 1 it is time to eat. } This callback function was DEFINED in Thread: 1 public void subscribe(Deadline. Event dle) { subscribers. add(dle); } public void run() { try { Thread. sleep(3000); } catch (Exception e 1) {} for (Deadline. Event e : subscribers) { e. callback(); } }} Try Me Deadline. Notifier notifier = new Deadline. Notifier(); long thread. ID = Thread. current. Thread(). get. Id(); Q: What will be printed? (more or less ) Callback ACTIVETED in Thread: 13} {Cow 2 it is time to eat. This callback function was DEFINED in Thread: 1 ACTIVETED in Thread: 13}
Multitasking vs. Multithreading 1. Shared Resources: • Memory space • Opened files and streams • Access rights 2. Context Switching. Introduction to threads
- Slides: 26