Concurrency
Concurrency
Threads
Similarities:
- Concurrency
- Execution planning
Differences:
- One thread can exist only inside of a process
- Process = code + data
- Thread = code
Defining Threads
The main() method, runs in one thread, called (surprisingly) the main thread. If you
look at the main call stack (in debugging mode), you'd see that main() is the first
method on the stack—the method at the bottom.
As soon as you create a new thread, a new stack materializes and methods called from
that thread run in a call stack that's separate from the main() call stack. That second
new call stack is said to run concurrently.
Defining Threads
Defining a thread:
- extend class Thread
- implement interface Runnable
Runnable
- Defines a common protocol for active objects
- Contains method run() - job always starts from a run() method
- It is implemented by class Thread
Defining Threads
Extending java.lang.Thread
The simplest way to define code to run in a separate thread is to
- Extend the java.lang.Thread class.
- Override the run() method.
• Keep in mind that you're free to overload the run() method in your Thread
subclass:
• The overloaded run(String s) method will be ignored by the Thread class unless
you call it yourself. The Thread class expects a run() method with no arguments,
and it will execute this method for you in a separate call stack after the thread has
been started.
• With a run(String s) method, the Thread class won't call the method for you, and
even if you call the method directly yourself, execution won't happen in a new
thread of execution with a separate call stack. It will just happen in the same call
stack as the code that you made the call from, just like any other normal method
call.
Instantiating Threads
Instantiating a thread:
If you call directly run() method, you don’t start a new thread with it’s own stack!!!
Defining Threads
Implementing java.lang.Runnable
Instantiating a thread:
Create a simple counting thread. It will count from start to end with a certain step.
Create 3 instances of the thread, that will count from 1 to 100 with the following
steps: 2, 3 and 5.
Set a name for each thread and when printing the numbers, print also the name of the
thread.
Thread States
New - state of the thread after the Thread instance has been created, but the
start() method has not been invoke.
Runnable - state of the thread when it's eligible to run, but the scheduler has not
selected it to be the running thread. A thread first enters the runnable state when the
start() method is invoked, but a thread can also return to the runnable state after either
running or coming back from a blocked, waiting, or sleeping state.
Thread States
Running - state of a thread when the thread scheduler selects it (from the runnable
pool) to be the currently executing process.
There are several ways to get to the runnable state, but only one way to get to the
running state: the scheduler chooses a thread from the runnable pool.
Thread States
Sleeping
The sleep() method is a static method of class Thread.
You use it in your code to "slow a thread down" by forcing it to go into a sleep mode
before coming back to runnable . When a thread sleeps, it drifts off somewhere and
doesn't return to runnable until it wakes up.
try {
Thread.sleep(5*60*1000); // Sleep for 5 minutes
} catch (InterruptedException ex) { }
Exercise
Create a simple counting thread. It will count to 100, pausing one second between
each number.
Also, it will output a string every ten numbers.
Thread Priorities
The scheduler does use thread priorities in one important way: If a thread enters the
runnable state, and it has a higher priority than any of the threads in the pool and a
higher priority than the currently running thread, the lower-priority running thread
usually will be bumped back to runnable and the highest-priority thread will be chosen
to run.
In other words, at any given time the currently running thread usually will not have a
priority that is lower than any of the threads in the pool. In most cases, the running
thread will be of equal or greater priority than the highest priority threads in the pool.
Thread Priorities
A daemon thread is a thread, that does not prevent the JVM from exiting when the
program finishes but the thread is still running.
You can use the setDaemon() method to change the Thread daemon properties.
When a Java Virtual Machine starts up, there is usually a single non-daemon thread (which
typically calls the method named main of some designated class).
The Java Virtual Machine continues to execute threads until either of the following occurs:
•The exit method of class Runtime has been called and the security manager has permitted
the exit operation to take place.
•All threads that are not daemon threads have died, either by returning from the call to the
run method or by throwing an exception that propagates beyond the run method.
Join
• The non-static join() method of class Thread lets one thread "join onto the end"of
another thread. If you have a thread B that can't do its work until another thread A
has completed its work, then you want thread B to "join" thread A. This means that
thread B will not become runnable until A has finished (and entered the dead state).
• The preceding code takes the currently running thread (if this were in the main()
method, then that would be the main thread) and joins it to the end of the thread
referenced by t. This blocks the current thread from becoming runnable until after
the thread referenced by t is no longer alive. In other words, the code t.join() means
"Join me (the current thread) to the end of t, so that t must finish before I (the
current thread) can run again."
Exercise
Its overall purpose is to only allow one thread at a time into a particular section of
code thus allowing us to protect, for example, variables or data from being
corrupted by simultaneous modifications from different threads
At its simplest level, a block of code that is marked as synchronized in Java tells the
JVM: "only let one thread in here at a time".
Synchronization
Synchronized static methods are synchronized on the class object of the class the
synchronized static method belongs to.
Since only one class object exists in the Java VM per class, only one thread can
execute inside a static synchronized method in the same class.
Synchronization
Every Java object created, including every Class loaded, has an associated lock or monitor.
Putting code inside a synchronized block makes the compiler append instructions to acquire
the lock on the specified object before executing the code, and release it afterwards (either
because the code finishes normally or abnormally).
Between acquiring the lock and releasing it, a thread is said to "own" the lock.
At the point of Thread A wanting to acquire the lock, if Thread B already owns it, then
Thread A must wait for Thread B to release it.
Disadvantages:
- it doesn't allow concurrent read, which can potentially limit scalability
- it can only be used to control access of shared object within the same JVM. If you have
more than one JVM and need to synchronized access to a shared file system or database,
the Java synchronized keyword is not at all sufficient
- performance cost
- deadlock or starvation
Synchronization
Producer/consumer
class Buffer {
private int number = -1;
public int get() {
return number;
}
value);
}
}
}
Share Resources
Use synchronized
wait - notify
class Buffer {
private int number = -1;
private boolean available = false;
public synchronized int get() {
while (!available) {
try {
wait(); // Wait for producer to put a value
} catch (InterruptedException e) {
e.printStackTrace();
}
}
available = false;
notifyAll();
return number;
}
Cooperation between tasks
wait(), notify(), and notifyAll() must be called from within a synchronized context!
A thread can't invoke a wait or notify method on an object unless it owns that object's
lock.