Computer
Science
School
Dr. Eliahu Khalastchi
2017
Advanced Software Development 2, Dr. Eliahu Khalastchi, 2017 ©
Computer
Science
School
In Java
Advanced Software Development 2, Dr. Eliahu Khalastchi, 2017 ©
Computer
Science
School
The Thread Life Cycle
A
Blocked
Scheduler choice
Ready Queue
End of Time slice Running
Born C
C
C Dead
B
[Link](x)
Sleep
[Link]()
Wait
Advanced Software Development 2, Dr. Eliahu Khalastchi, 2017 ©
Computer
Science
School
Thread & Runnable
<<Runnable>>
void run()
Thread
Runnable r;
void run() {
[Link]();
}
void start() Tells the JVM to execute run() in a thread
… i.e., run() enters the Ready Queue
Advanced Software Development 2, Dr. Eliahu Khalastchi, 2017 ©
Computer
Science
School
Option 1: extending Thread
Thread
run()
start()
1. Extended the Thread class
2. Override the run() method
3. Call start to execute in parallel
MyTask
run(){ doTask(); }
But sometimes our class is not a type of Thread or it already extends something else
Advanced Software Development 2, Dr. Eliahu Khalastchi, 2017 ©
Computer
Science
School
Option 2: implementing Runnable
Thread <<Runnable>>
start() run()
1. Implement the Runnable interface
2. Create an instance of Thread
3. Inject the Runnable
4. Call start
MyTask
run(){ doTask(); }
This is a typical strategy pattern, but what if we don’t want to (or can’t) change MyTask?
Advanced Software Development 2, Dr. Eliahu Khalastchi, 2017 ©
Computer
Science
School
Option 3: using object adapters!
Thread <<Runnable>> Thread t=new Thread(new TaskRunnable(new MyTask()));
start() run() [Link]();
TaskRunnable
<<Task>>
Task t; doTask()
run(){ [Link](); }
MyTask
Advanced Software Development 2, Dr. Eliahu Khalastchi, 2017 ©
Computer
Science
School
Advanced Software Development 2, Dr. Eliahu Khalastchi, 2017 ©
Computer
Science
School
Advanced Software Development 2, Dr. Eliahu Khalastchi, 2017 ©
Computer
Science
School
Active Object
Decouples method execution from method invocation
for objects that each reside in their own thread of control
The goal is to introduce concurrency,
by using asynchronous method invocation
and a scheduler for handling requests
Advanced Software Development 2, Dr. Eliahu Khalastchi, 2017 ©
Computer
Science
School
Example
class MyModel implements Model{
Maze maze;
Solution solution;
Not an active object
void generateMaze(){ Method invocation is coupled to execution
maze=[Link](/**/);
}
void solve(Maze m){
solution=[Link](m);
}
}
Advanced Software Development 2, Dr. Eliahu Khalastchi, 2017 ©
Computer
Science
School
Example AMI – asynchronous method invocation
class MyActiveModel implements Model { void generateMaze() throws InterruptedException {
[Link](new Runnable() {
Maze maze; public void run() {
Solution solution; maze = [Link](/**/);
BlockingQueue<Runnable> dispatchQueue }
= new LinkedBlockingQueue<Runnable>(); });
}
public MyActiveModel() {
void solve(Maze m) throws InterruptedException {
new Thread(new Runnable() { [Link](new Runnable() {
public void run() { public void run() {
while (true) { solution = [Link](m);
try { }
// take() blocks, so no busy waiting });
[Link]().run(); }
} catch (InterruptedException e) {}
}
}
}).start();
}
Advanced Software Development 2, Dr. Eliahu Khalastchi, 2017 ©
Computer
Science
School
Advanced Software Development 2, Dr. Eliahu Khalastchi, 2017 ©
Computer
Science
School
Double-checked locking
Goal: to reduce the overhead of acquiring a lock
by first testing the locking
without actually acquiring the lock
Only if the locking is required then do the actual locking
Advanced Software Development 2, Dr. Eliahu Khalastchi, 2017 ©
Computer
Science
School
Example - Singleton
class Foo {
private Helper helper;
public Helper getHelper() {
if (helper == null) {
helper = new Helper();
}
return helper;
}
}
Advanced Software Development 2, Dr. Eliahu Khalastchi, 2017 ©
Computer
Science
School
Example - Singleton
class Foo {
private Helper helper;
public synchronized Helper getHelper() {
if (helper == null) {
helper = new Helper();
}
return helper;
}
}
Advanced Software Development 2, Dr. Eliahu Khalastchi, 2017 ©
Computer
Science
School
Example - Singleton
class Foo {
private Helper helper;
public Helper getHelper() {
if (helper == null) {
synchronized(this) {
if (helper == null) {
helper = new Helper();
}
}
}
return helper;
}
}
Advanced Software Development 2, Dr. Eliahu Khalastchi, 2017 ©
Computer
Science
School
Example - Singleton
class Foo {
private Helper helper; helper
public Helper getHelper() {
if (helper == null) { Thread B
synchronized(this) {
if (helper == null) {
helper = new Helper(); Thread A Helper
}
}
}
return helper;
}
}
Advanced Software Development 2, Dr. Eliahu Khalastchi, 2017 ©
Computer
Science
School
Volatile
Every read & write to a volatile variable will be on the main memory
Not the CPU cache…
CPU 1 CPU 1 Main
Cache Memory
Thread A c=7
c=0
c=0
c=7 // none volatile
CPU 2 CPU 2
Cache
Thread B c=0
Advanced Software Development 2, Dr. Eliahu Khalastchi, 2017 ©
Computer
Science
School
Volatile
Every read & write to a volatile variable will be on the main memory
Not the CPU cache…
CPU 1 CPU 1 Main
Cache Memory
Thread A
c=0
c=7 // volatile
CPU 2 CPU 2
Cache
Thread B
Advanced Software Development 2, Dr. Eliahu Khalastchi, 2017 ©
Computer
Science
School
Volatile: Happens-Before Guarantee
Every read & write to a volatile variable will be on the main memory
Not the CPU cache…
When a thread reads or writes to a volatile variable
all other dependent variables are flushed to main memory as well
Reading and writing instructions cannot be reordered by the JVM
Advanced Software Development 2, Dr. Eliahu Khalastchi, 2017 ©
Computer
Science
School
Example - Singleton
class Foo {
private volatile Helper helper; helper = null
public Helper getHelper() {
if (helper == null) { Thread B
synchronized(this) {
if (helper == null) {
helper = new Helper(); Thread A Helper
}
}
}
return helper;
}
}
Advanced Software Development 2, Dr. Eliahu Khalastchi, 2017 ©
Computer
Science
School
Example - Singleton
class Foo{
private volatile Helper helper;
public Helper getHelper() {
Helper result = helper;
if (result == null) {
synchronized(this) {
result = helper;
if (result == null) {
helper = result = new Helper();
}
}
}
return result; As much as 25% performance improvement
}
}
Advanced Software Development 2, Dr. Eliahu Khalastchi, 2017 ©
Computer
Science
School
Advanced Software Development 2, Dr. Eliahu Khalastchi, 2017 ©
Computer
Science
School
Example - Singleton
class Foo{
“Eager” instead of “Lazy”
private static final Helper helper = new Helper();
public static Helper getHelper() {
return helper;
}
}
Advanced Software Development 2, Dr. Eliahu Khalastchi, 2017 ©
Computer
Science
School
Example - Singleton
class Foo{
private static class HelperHolder {
public static final Helper helper = new Helper();
}
public static Helper getHelper() {
return [Link];
} inner classes are not loaded until they are referenced
}
Advanced Software Development 2, Dr. Eliahu Khalastchi, 2017 ©
Computer
Science
School
Advanced Software Development 2, Dr. Eliahu Khalastchi, 2017 ©
Computer
Science
School
Executor Implementations Example
interface Executor {
void execute(Runnable r);
}
class DirectExecutor implements Executor{ class ThreadPerTaskExecutor implements Executor{
public void execute(Runnable r) { public void execute(Runnable r) {
[Link](); new Thread(r).start();
} }
} }
And if we wanted to control the number of threads?
Advanced Software Development 2, Dr. Eliahu Khalastchi, 2017 ©
Computer
Science
School
Thread Pools Example
public class RunnableTask1 implements Runnable{ import [Link];
public void run(){ import [Link];
[Link]("task1 started"); import [Link];
try { [Link](10000);} //...
catch (InterruptedException e) {} public static void main(String[] args) {
[Link]("task1 finished"); ExecutorService executor =
} [Link](2);
} [Link] (new RunnableTask1 ());
// RunnableTask2 & RunnableTask3 are the same… [Link] (new RunnableTask2 ());
[Link] (new RunnableTask3 ());
task1 started }
task2 started
task1 finished
task2 finished
task3 started
task3 finished
Advanced Software Development 2, Dr. Eliahu Khalastchi, 2017 ©
Computer
Science
School
Thread Pool
Control the number of threads
No thread creation / destruction overhead
// a thread that can run task after task
class PooledThread extends Thread{
Runnable task;
Object lock;
boolean terminated=false;
public void assignTask(Runnable r){
task=r;
unSuspendMe();
}
public void run(){
while(!terminated){
[Link]();
suspendMe();
}
} // the pooled thread dies
// ...
Advanced Software Development 2, Dr. Eliahu Khalastchi, 2017 ©
Computer
Science
School
• doesn't block the calling thread while waiting for a reply
• Instead, the calling thread is notified when the reply arrives
• Polling for a reply is an undesired option.
• One common use of AMI is in the active object design pattern
• Alternatives are synchronous method invocation and future objects.
Advanced Software Development 2, Dr. Eliahu Khalastchi, 2017 ©
Computer
Science
School
Callable
Runnabler’s run() method
interface Callable<V> {
Cannot return a value
Cannot throw an exception V call() throws Exception;
A Callable Interface can }
ExecutorService (a type of thread pool) can:
execute(Runnable r); // as we have seen
submit(Callable c);
It puts the callable in the thread pool and immediately returns
What can be returned by submit?
Advanced Software Development 2, Dr. Eliahu Khalastchi, 2017 ©
Computer
Science
School
The problem
public class MyCallable implements Callable<Worker>{
Worker call() throws Exception{
// after 10 minutes or so…
return someWorker;
}
}
ExecutorService executor = Executors. newFixedThreadPool (2);
___________ = [Link] (new MyCallable ());
1. The submit() method was written years ago… the Worker class was created just now…
2. submit() should return a value now! And not in 10 minutes
Advanced Software Development 2, Dr. Eliahu Khalastchi, 2017 ©
Computer
Science
School
The Solution – Future! Future <V>
V value;
Future is a holder for a value of type <V> set(V v);
V get();
The submit method returns immediately an instance of Future
Future<V> submit(Callable<V> callable);
We should define the same V in the Callable and the Future
When the Callable’s call() returns <V> it is set in the instance of Future
Only then, we may get <V>
Advanced Software Development 2, Dr. Eliahu Khalastchi, 2017 ©
Computer
Science
School
The Solution – Future! Future <V>
V value;
public class MyCallable implements Callable<Worker>{ set(V v);
V get();
Worker call() throws Exception{
// after 10 minutes or so…
return someWorker;
}
}
ExecutorService executor = Executors. newFixedThreadPool (2);
Future<Worker> f = [Link] (new MyCallable ());
// ...
Worker w = [Link](); // waits for the call() to return
Guarded suspension pattern
Advanced Software Development 2, Dr. Eliahu Khalastchi, 2017 ©
Computer
Science
School
Advanced Software Development 2, Dr. Eliahu Khalastchi, 2017 ©
Computer
Science
School
Guarded Suspension
Manages operations that require both
a lock to be acquired public class GameCharacter {
boolean victory;
and a precondition to be satisfied int score;
before the operation can be executed synchronized void victoryDance() { // guarded method
while (!victory) {
try { wait();} catch (InterruptedException e) {}
}
// Actual task implementation
// victory dance!!
}
synchronized void updateScore(int x) {
// ...
// Inform waiting threads
notify();
}
}
Advanced Software Development 2, Dr. Eliahu Khalastchi, 2017 ©