4. Thread Interference
Threads are lightweight processes and also provide an
execution environment
Threads share the process's resources, including
memory . This makes for efficient, but potentially
problematic, communication.
Interference happens when two operations, running in
different threads, but acting on the same
data, interleave.
This means that the two operations consist of multiple
steps, and the sequences of steps overlap.
Onkar Deshpande 4
5. Memory Consistency Errors
Memory consistency errors occur when different threads
have inconsistent views of what should be the same data.
Happens-before relationship between these two
statements.
eg: int counter = 0;
The counter field is shared between two threads, A and B.
thread A increments counter:counter++;
Then, shortly afterwards, thread B prints out counter:
System.out.println(counter);
There are several actions that create happens-before
relationships. One of them is synchronization
Onkar Deshpande 5
6. Synchronized Methods
Synchronized methods has two effects:
Only one thread is allowed to access synchronized methods
at particular instant.
It automatically establishes a happens-before relationship
with any subsequent invocation of a synchronized method
for the same object. This guarantees that changes to the
state of the object are visible to all threads.
Synchronized methods prevents thread interference
and memory consistency errors: if an object is visible to
more than one thread, all reads or writes to that
object's variables are done
through synchronized methods.
Onkar Deshpande 6
7. Intrinsic Locks and Synchronization
A thread needs to acquire the object's intrinsic lock
before accessing them, and then release the intrinsic
lock when it's done
When a thread releases an intrinsic lock, a happens-
before relationship is established between that action
and any subsequent acquisition of the same lock.
Synchronized Block:
synchronized(this) {
}
Onkar Deshpande 7
8. Atomic Access
Atomic action either happens completely, or it doesn't
happen at all.
Reads and writes are atomic for reference variables and
for most primitive variables (all types except long and
double).
Reads and writes are atomic for all variable declared
volatile (including long and double variables).
Onkar Deshpande 8
9. Atomic Access
Using volatile variables reduces the risk of memory
consistency errors, because any write to a volatile
variable establishes a happens-before relationship with
subsequent reads of that same variable.
Simple atomic variable access is more efficient than
accessing these variables through synchronized code,
Onkar Deshpande 9
14. Re-entrant Read Write Lock
The conditions for getting read and write access to the
resource:
Read Access Write Access
If no threads are writing, and no threads
have requested write access.
If no threads are reading or writing.
Onkar Deshpande 14
15. Intrinsic v/s Extrinsic
Same mechanism for locking
Performance improvement is purely subjective
Extrinsic locks give a more explicit control mechanism
for better handling of deadlocks, starvation, and so on.
Onkar Deshpande 15
16. Problems with locking
Performance degradation
Priority Inversion
Deadlock
Heavy weight stuff
Onkar Deshpande 16
17. Lock-free and wait-free algorithms
Also known as Non-blocking algorithms
Based on CAS
Java.util.concurrent package
Onkar Deshpande 17
19. Module 3: Liveness
Overview
Deadlock
How to avoid deadlock?
Starvation & Livelock.
Onkar Deshpande 19
20. What is Deadlock?
Deadlock describes a situation where two or more threads are
blocked forever, waiting for each other.
Deadlock occurs when multiple threads need the same locks but
obtain them in different order.
It can only happen when the program is running multiple threads,
and multiple locks are being used by multiple threads.
A single-threaded program will never have deadlocks.
A program with one lock will never have deadlocks.
Onkar Deshpande 20
21. How to avoid deadlock ?
The simplest and most efficient way to avoid deadlock
is to ensure that resources are always acquired in some
well-defined order.
Another way is to prevent the potential for deadlock by
acquiring more than one lock at a time.
Avoid Nested locks.
It’s always best to use thread.join() with maximum time
you want to wait for the other thread to finish.
Onkar Deshpande 21
22. Starvation
What is Starvation?
Its causes:
Inappropriate thread priorities.
Synchronize method which takes lot of time to return.
How its different from deadlock?
How to prevent Starvation?
Priority scheme can be used to ensure that starvation
does not occur due to a scheduler.
Queuing disciplines (such as FIFO) can guarantee
starvation will not occur.
Onkar Deshpande 22
23. Livelock
What is Livelock?
Livelock is a situation in which a non-blocked thread cannot
make any progress because it keeps retrying an operation
that will always fail.
How its different from deadlock?
Detection is less obvious from simple observation than
deadlock due to the fact that the program counter doesn’t
freeze.
Livelock results in CPU cycles wastage.
Onkar Deshpande 23
25. Guarded Blocks
A Guarded block is a strategy to achieve co-ordination
between Threads which attempt to modify shared data
Such blocks are controlled by a condition which
determines whether or not the shared data can be
modified
Onkar Deshpande 25
26. Guarded Blocks
Real life example where a guarded block could be used
John and Jenny hold a joint account in a Bank, with USD
10000 account balance.
One fine day Jenny deposits USD 1000 by cash to her
account.
While her account is updating, her husband John deposits
USD 1000 to her account almost at the same time from
another branch.
Her balance is now updated to USD 11000 thereby ignoring
her last deposit of USD 1000. Basically we lost one update
which happened on her account!
This problem has occurred because two transactions are
working on the same resource without knowing each other’s
activity.
Onkar Deshpande 26
27. Guarded Blocks example
public void readBalance() {
try {
System.out.println(Thread.currentThread().getName() + " " + new Date() + "
Current balance " + balance);
Thread.sleep(1000);
} catch (Exception ex) {ex.printStackTrace();}
}
public void deposit(float amount) {
try {
balance += amount;
System.out.println(Thread.currentThread().getName() + " " + new Date()
+ " New balance " + balance);
} catch (Exception ex) {ex.printStackTrace();}
}
Onkar Deshpande 27
Reading the Balance
Making the deposit
Result
Jenny Wed Aug 06 08:43:32 IST 2014 Current balance 10000.0
John Wed Aug 06 08:43:32 IST 2014 Current balance 10000.0
Jenny Wed Aug 06 08:43:33 IST 2014 New balance 11000.0
John Wed Aug 06 08:43:33 IST 2014 New balance 11000.0
28. Example
boolean accountReady = true;
//This method will be called before deposit is made
public synchronized void verify() {
while (!accountReady) {
try {
System.out.println(Thread.currentThread().getName() + " " + new Date()
+ " Waiting for account to be ready");
wait();
} catch (InterruptedException ex) {ex.printStackTrace();}}
System.out.println(Thread.currentThread().getName() + " " + new Date() + " Account is now
ready");
}
//This method will be called after deposit is made
public synchronized void readyAccount() {
accountReady = true;
notifyAll();
}
Onkar Deshpande 28
Guarding condition
29. Guarded Blocks example
public void deposit(float amount) {
try {
verify();
accountReady = false;
Thread.sleep(2000);
balance += amount;
System.out.println(Thread.currentThread().
getName() + " "+ new Date() + " New balance " + balance);
} catch (Exception ex) {
ex.printStackTrace();
}
readyAccount();
}
Onkar Deshpande 29
Output
Jenny Wed Aug 06 09:18:21 IST 2014 Current balance 10000.0
John Wed Aug 06 09:18:21 IST 2014 Current balance 10000.0
Jenny Wed Aug 06 09:18:22 IST 2014 Account is now ready
John Wed Aug 06 09:18:22 IST 2014 Waiting for account to be ready
Jenny Wed Aug 06 09:18:24 IST 2014 New balance 11000.0
John Wed Aug 06 09:18:24 IST 2014 Account is now ready
John Wed Aug 06 09:18:26 IST 2014 New balance 12000.0
Making the deposit
30. Immutable Objects
An object is considered immutable if its state cannot
change after it is constructed.
If we try to modify the immutable object ,will get the
another immutable object as result. String class is an
example of immutable object.
Immutable objects are by default thread-safe, can be
shared without synchronization in concurrent
environment.
Onkar Deshpande 30
31. Creating your own Immutable Objects
Set the values of properties using constructor only,
Avoid setters for fields.
Create a final class . Make all fields final and private.
If the instance fields include references to mutable
objects, don't allow those objects to be changed:
Don't provide methods that modify the mutable objects.
Don't share references to the mutable objects. Never store
references to external, mutable objects passed to the
constructor; if necessary, create copies, and store references
to the copies. Similarly, create copies of your internal
mutable objects when necessary to avoid returning the
originals in your methods.
Onkar Deshpande 31
33. Lock Objects
Synchronized Blocks and Monitors with
java.lang.Thread class are adequate for very basic tasks,
but higher-level building blocks are needed for more
advanced tasks.
High-level concurrency features are introduced with
java 5.0 Most of these features are implemented in the
new java.util.concurrent
Lock implementations provide more extensive locking
operations compared to Synchronized methods and
blocks.
Onkar Deshpande 33
34. Interface Lock (java.util.concurrent.Locks): Method Summary
Modifier and Type Method and Description
void lock()
Acquires the lock.
void lockInterruptibly()
Acquires the lock unless the current thread is interrupted.
Condition newCondition()
Returns a new Condition instance that is bound to
this Lock instance.
boolean tryLock()
Acquires the lock only if it is free at the time of invocation.
boolean tryLock(long time, TimeUnit unit)
Acquires the lock if it is free within the given waiting time and
the current thread has not been interrupted.
void unlock()
Releases the lock.
Onkar Deshpande 34
35. Case of Deadlock
import java.io.FileInputStream;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockDemo {
static Lock lock = new ReentrantLock();
void f () throws Exception {
lock.lock();
FileInputStream f = new FileInputStream("file.txt");
// Do something with file
f.close();
lock.unlock();
}
}
Lock not released if exception thrown
Likely to cause deadlock some time later
Onkar Deshpande 35
36. Solution: Use Finally
import java.io.FileInputStream;
import java.util.concurrent.locks.*;
public class LockDemo {
static Lock lock = new ReentrantLock();
void f() throws Exception {
lock.lock();
try {
FileInputStream f = new FileInputStream("file.txt");
// Do something with f
f.close();
} finally {
// This code executed no matter how we exit the try block
lock.unlock();
}
}
}
Onkar Deshpande 36
37. Threads pools with the Executor Framework
Executors framework
Released with the JDK 5
For large-scale applications, it makes sense to separate
thread management and creation from the rest of the
application.
Objects that encapsulate these functions are known
as Executors.
Used to run the Runnable objects without creating new
threads every time and mostly re-using the already created
threads.
Onkar Deshpande 37
38. Thread pool
Collection of Runnable objects (work queue) & connections
of running threads (worker threads).
Pool of worker threads, which is ready to perform any task.
These threads are constantly running and are checking the
work queue for new work. If there is new work to be done
they execute this Runnable.
Core of this thread pool framework is Executor interface
which defines abstraction of task execution with
method execute(Runnable task) and ExecutorService which
extends Executor to add various life-cycle and thread pool
management facilities like shutting down thread pool.
Onkar Deshpande 38
39. Executor Interfaces
The java.util.concurrent package defines three executor
interfaces:
The Executor Interface: provides a single method, execute(). If r is
a Runnable object, and e is an Executor object you can replace
(new Thread(r)).start() with e.execute(r)
The ExecutorService Interface: provides submit method and
other thread life cycle methods.
submit(Callable <T> task)
submit(Runnable task)
submit(Runnable task, T result)
void shutdown()
boolean isShutdown()
The ScheduledExecutorService Interface: provides methods of
it’s parent ExecutorService with Schedule.
Onkar Deshpande 39
40. Implementing Thread Pool
One common type of thread pool is the fixed thread pool. Below
are related classes.
java.util.concurrent.Executors
The newCachedThreadPool method creates an executor with an expandable
thread pool. This executor is suitable for applications that launch many short-
lived tasks.
The newSingleThreadExecutor method creates an executor that executes a
single task at a time.
Several factory methods are ScheduledExecutorService versions of the above
executors.
java.util.concurrent.ThreadPoolExecutor
We can specify the number of threads that will be alive when we create
ThreadPoolExecutor instance and we can limit the size of thread pool and
create our own RejectedExecutionHandler implementation to handle the jobs
that can’t fit in the worker queue.
java.util.concurrent.ScheduledThreadPoolExecutor
An ExecutorService which can schedule tasks to run after a delay, or to execute
repeatedly with a fixed interval of time in between each execution.
Onkar Deshpande 40
41. Fork/Join Framework
Fork/Join framework
Released with the JDK 7
Distribute the work across multiple cores and then join them
to return the result set
Fork-Join breaks the task at hand into mini-tasks.
No worker thread is idle.
Implement a work-stealing algorithm(Unlike the Executor
framework)
Onkar Deshpande 41
43. Example of Fork/Join Pool Framework
A program that will search for files with a determined
extension inside a folder and its subfolders
The ForkJoinTask class will process the content of a
folder.
For each subfolder inside that folder, it will send a new
task to the ForkJoinPool class in an asynchronous way.
Onkar Deshpande 43
44. Existing Implementations in JDK
java.util.Arrays class for its parallelSort() methods in
JDK 8.
Parallel sorting of large arrays is faster than sequential
sorting when run on multiprocessor systems.
Onkar Deshpande 44
46. Module 6: Concurrent Collection
Overview
What are Concurrent Collections?
Need of Concurrent Collections
Concurrent Collection Classes
Example
Onkar Deshpande 46
47. What are Concurrent Collections?
Introduced in Java 1.5
Java Collections reside inside java.util package where
as Concurrent Collections reside inside
java.util.concurrent package
Designed for concurrent access from multiple threads
Purpose of these classes is to provide high-
performance, highly scalable, thread-safe versions of
the basic collection types.
Onkar Deshpande 47
48. Need of Concurrent Collections
In Synchronized Collections, every method is
synchronized on a common lock, restricting access to a
single thread at a time.
Concurrent Collections uses a finer-grained locking
mechanism called lock striping.
Onkar Deshpande 48
50. Example
public static void main(String[] args){
List list = new ArrayList();
list.add("A");
Iterator i =list.iterator();
while(i.hasNext()){
System.out.println(i.next());
list.add("B"); // throws ConcurrentModificationException
}
System.out.println("After modification:");
Iterator i2 =list.iterator();
while(i2.hasNext()){
System.out.println(i2.next());
i2.remove(); // No UnsupportedOperationException is thrown
}
}
Onkar Deshpande 50
51. Example
public static void main(String[] args){
CopyOnWriteArrayList list = new CopyOnWriteArrayList();
list.add("A");
Iterator i =list.iterator();
while(i.hasNext()){
System.out.println(i.next());
list.add("B"); // No ConcurrentModificationException is thrown
}
System.out.println("After modification:");
Iterator i2 =list.iterator();
while(i2.hasNext()){
System.out.println(i2.next());
i2.remove(); // throws UnsupportedOperationException
}
}
Onkar Deshpande 51
52. Synchronous Queue
It is a special kind of queue where producer waits until the
consumer is ready and consumer waits until the producer is
ready.
It can contain only a single element internally.
When you call put() method on Synchronous Queue it blocks
until another thread is there to take that element out of the
Queue.
Similarly, if a thread tries to remove an element and no
element is currently present, that thread is blocked until
another thread puts an element into the queue.
Onkar Deshpande 52
Put Thread Take Thread
Synchronous
Queue
53. Example
Thread producer = new Thread("PRODUCER") {
public void run() {
String event = "FOOD";
try {
queue.put(event); // thread will block here
System.out.printf("[%s] published event :
%s %n", Thread .currentThread().getName(),
event);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
producer.start(); // starting publisher thread
Onkar Deshpande 53
54. Example
Thread consumer = new Thread("CONSUMER") {
public void run() {
try {
String event = queue.take(); // thread will
block here
System.out.printf("[%s] consumed event :
%s %n", Thread .currentThread().getName(),
event);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
consumer.start(); // starting consumer thread
Onkar Deshpande 54
Output:
[PRODUCER] published
event : FOOD
[CONSUMER] consumed
event : FOOD
55. Things to remember about Synchronous Queue
SynchronousQueue is used to implement queuing
strategy of direct hand-off, where thread hands-off to
waiting thread.
This queue does not permit null elements, adding null
elements will result in NullPointerException.
SynchronousQueue has zero capacity.
You cannot peek at a SynchronousQueue because an
element is only present when you try to take it.
You cannot iterate over SynchronousQueue as there is
nothing to iterate.
Constructed with fairness policy set to true grants
threads access in FIFO order.
Onkar Deshpande 55
56. BlockingQueue
Blocking queue means the Operation on the queue block when
the Queue is empty or full.
Happen Before relationship
Producer consumer design pattern implementations.
extends Iterable, Collection and Queue interfaces.
If capacity not decided default is Integer.MAX_VALUE.
Does not accept null values
Onkar Deshpande 56
61. BlockingDeque
Extends the properties of BlockingQueue and Deque.
Retrieval and deletion possible from both ends.
Onkar Deshpande 61
62. TransferQueue ?
It extends BlockingQueue.
It is a refinement of the BlockingQueue interface in
which producers can wait for consumers to receive
elements.
TransferQueue is useful in scenario where message
passing need to be guaranteed.
The main method of TransferQueue is transfer(E e) .
Onkar Deshpande 62
63. Transfer(E e) method in TransferQueue
If, there is an consumers waiting to take element, then
producer directly transfers the element to consumer.
If there is no consumer waiting, then producer will not
directly put the element and returned, but it will wait for
any consumer to consume the element.
Onkar Deshpande 63
Producer Consumer
TranferQueue
Transferring element
64. TranferQueue v/s BlockingQueue
BlockingQueue: can only put element into queue (and
block if queue is full).
TransferQueue: can also block until other thread
receives your element (using new transfer method for
that).
Onkar Deshpande 64
65. TransferQueue Implementation
LinkedTransferQueue
Backed by Linked Node
Unbounded TransferQueue (no size restriction).
Follows first-in-first-out (FIFO) concept to get and add
Element.
Onkar Deshpande 65
66. TransferQueue Implementation
Some methods of LinkedTransferQueue.
transfer(E e) : Inherited from TransferQueue in which
producer transfers the element to consumer and waits if
necessary.
put(E e) : producers put the element and it does not throw
exception because there is no size restriction.
take() : It retrieves the element from queue and wait if
empty.
Onkar Deshpande 66
67. Example
static LinkedTransferQueue<String> lnkTransQueue = new LinkedTransferQueue<String>();
public static void main(String[] args)
{
ExecutorService exService = Executors.newFixedThreadPool(2);
Producer producer = new LinkedTransferQueueExample().new Producer();
Consumer consumer = new LinkedTransferQueueExample().new Consumer();
exService.execute(producer);
exService.execute(consumer);
exService.shutdown(); // Cancel currently executing tasks
}
Onkar Deshpande 67
68. Example
class Producer implements Runnable {
@Override
public void run() {
for (int i = 0; i < 3; i++) {
try {
System.out.println("Producer is waiting to
transfer...");
lnkTransQueue.transfer("A" + i);
System.out.println("producer transfered element: A" + i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Onkar Deshpande 68
69. Example
class Consumer implements Runnable {
@Override
public void run() {
for (int i = 0; i < 3; i++) {
try {
System.out.println("Consumer is waiting to take
element...");
String s = lnkTransQueue.take();
System.out.println("Consumer received Element: " + s);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Onkar Deshpande 69
70. Output
Output:
Producer is waiting to transfer...
Consumer is waiting to take element...
Consumer received Element: A0
Consumer is waiting to take element...
producer transfered element: A0
Producer is waiting to transfer...
producer transfered element: A1
Producer is waiting to transfer...
Consumer received Element: A1
Consumer is waiting to take element...
Consumer received Element: A2
producer transfered element: A2
Onkar Deshpande 70
72. Atomic Numbers
What are Atomic variables?
What’s wrong with traditional approach.
Necessity of atomic variables.
Example
Onkar Deshpande 72
73. Example
public void increment() {
boolean valueChanged = false;
while(!valueChanged) {
int currentVal = count.get();
int nextVal = currentVal + 1;
if(count.compareAndSet(currentVal,nextVal)) {
valueChanged = true;
}
}
}
//Non-blocking CAS approach
Onkar Deshpande 73
74. Concurrent Random Numbers
Onkar Deshpande 74
GLOBAL RANDOM
GENERATOR
Math.random()
T1 T2 T3 T4
Random
Number
Generator
T2
Random
Number
Generator
T2
75. Math.random() v/s ThreadLocalRandom
Random Number Generator
Overheads / Performance
Use Case scenario
When Used
ThreadLocalRandom.current().nextDouble();
Time required to calculate Random number by t1 :: 379
Time required to calculate Random number by t2 :: 401
Time required to calculate Random number by t3 :: 400
When Used Math.random();
Time required to calculate Random number by t1 :: 430
Time required to calculate Random number by t2 :: 464
Time required to calculate Random number by t3 :: 576
Onkar Deshpande 75
76. Semaphore & Permits
What is Semaphore:
A semaphore is a variable or abstract data type that is used for
controlling access, by multiple processes, to a common resource
in a parallel programming or a multi user environment.
A useful way to think of a semaphore is as a record of how many
units of a particular resource are available, coupled with
operations to safely adjust that record as units are required or
become free, and, if necessary, wait until a unit of the resource
becomes available.
Example: Library Analogy
Key Points :
Not releasing after acquire (either missing release call or an
exception is thrown and there is no finally block)
Long held semaphores, causing thread starvation
Deadlocks
Onkar Deshpande 76
77. Semaphore and Permits
Permits: Availability count of resource is permit.
It is an integer count set while initializing Semaphore.
Onkar Deshpande 77
80. Monitors
Monitors are an other mechanism of concurrent
programming.
It's a higher level mechanism than semaphores and also
more powerful
Onkar Deshpande 80
81. Monitors
A monitor is concurrency control construct used for
synchronization and scheduling
It allows thread to have mutual exclusion
And the ability to wait (block) for certain condition to
become true
It also allows notifying other threads that their condition
have been met.
Note : All objects in java can be used as built-in monitor
objects.
Onkar Deshpande 81
83. Flow of Execution (Example)
SETTING DATA :
Obtain Lock
If full then wait till it is empty
Else set data and signal those threads who were waiting for
data to be set
GETTING DATA
Obtain Lock
If empty then wait till it is full
Else get data and signal those threads who were waiting for
data to be set
Onkar Deshpande 83
84. Locks and Condition - Example
private volatile boolean usedData = true;//mutex for data
private final Lock lock = new ReentrantLock();
private final Condition isEmpty = lock.newCondition();
private final Condition isFull = lock.newCondition();
public void setData(int data) throws InterruptedException {
lock.lock();
try {
while(!usedData) {//wait for data to be used
isEmpty.await();
}
this.data = data;
isFull.signal();//broadcast that the data is now full.
usedData = false;//tell others I created new data.
}finally {
lock.unlock();//interrupt or not, release lock
}
}
Onkar Deshpande 84
85. Locks and Condition - Example
public void getData() throws InterruptedException{
lock.lock();
try {
while(usedData) {//usedData is lingo for empty
isFull.await();
}
isEmpty.signal();//tell the producers to produce some more.
usedData = true;//tell others I have used the data.
}finally {//interrupted or not, always release lock
lock.unlock();
}
}
Onkar Deshpande 85
86. What is a CountDown Latch ?
A synchronization aid that allows one or more threads
to wait until a set of operations being performed in
other threads complete.
CountDownLatch was introduced with JDK 1.5 along
with other concurrent utilities like CyclicBarrier,
Semaphore, Councurrent
HashMap and BlockingQueue in java.util.concurrent
package.
Onkar Deshpande 86
87. How it works ?
CountDownLatch works by
having a counter initialized
with number of threads,
which is decremented each
time a thread complete its
execution.
When count reaches to
zero, it means all threads
have completed their
execution, and thread
waiting on latch resume the
execution.
Onkar Deshpande 87
88. Sample Code
public class CountDownLatchDemo {
public static void main(String args[]) {
final CountDownLatch latch = new CountDownLatch(3);
Thread cacheService = new Thread(new Service("CacheService", 1000, latch));
Thread alertService = new Thread(new Service("AlertService", 1000, latch));
Thread validationService = new Thread(new Service("ValidationService", 1000, latch));
cacheService.start(); //separate thread will initialize CacheService
alertService.start(); //another thread for AlertService initialization
validationService.start();
try{
latch.await(); //main thread is waiting on CountDownLatch to finish
System.out.println("All services are up, Application is starting now");
}catch(InterruptedException ie){
ie.printStackTrace();
}
}
}
Onkar Deshpande 88
89. Sample Code
public class Service implements Runnable {
private final String name;
private final int timeToStart;
private final CountDownLatch latch;
public Service(String name, int timeToStart, CountDownLatch latch) {
this.name = name;
this.timeToStart = timeToStart;
this.latch = latch;
}
@Override
public void run() {
try {
Thread.sleep(timeToStart);
} catch (InterruptedException ex) {
System.out.println("Error in execution :: "+ Service.class.getName());
}
System.out.println(name + " is Up");
latch.countDown(); // reduce count of CountDownLatch by 1
}
}
Onkar Deshpande 89
90. Use Cases
Achieving Maximum Parallelism : Sometimes we want
to start a number of threads at the same time to
achieve maximum parallelism.
Wait N threads to completes before start execution :
Any server side core Java application which uses
services architecture, where multiple services is
provided by multiple threads and application can not
start processing until all services have started
successfully.
Deadlock detection : A very handy use case in which
you can use N threads to access a shared resource with
different number of threads in each test phase, and try
to create a deadlock.
Onkar Deshpande 90
There are different types of DS. Most commonly used are Arrays, List (Dynamic Arrays), Hashes (uses hashing algorithm), Trees (Binary Search Tree, Red-Black Tree), Graph (arrangement of edges and vertices).
When solving a computer science problem there will usually be more than just one solution. These solutions will often be in the form of different algorithms, and you will generally want to compare the algorithms to see which one is more efficient.
This is where Big O analysis helps – it gives us some basis for measuring the efficiency of an algorithm.
Volatile - Volatile variables can also be used to store shared variables at a lower cost than that of synchronization, but they have limitations. While writes to volatile variables are guaranteed to be immediately visible to other threads, there is no way to render a read-modify-write sequence of operations atomic, meaning, for example, that a volatile variable cannot be used to reliably implement a mutex (mutual exclusion lock) or a counter.
Lock Interruptibly - It is not possible to interrupt a thread waiting to acquire a lock
Try Lock - It is not possible to attempt to acquire a lock without being willing to wait for it forever
Cannot implement non-block-structured locking disciplines, as intrinsic locks must be released in the same block in which they are acquired.
In Java 5.-, a new addition called Reentrant Lock was made to enhance intrinsic locking capabilities. Prior to this, "synchronized" and "volatile" were the means for achieving concurrency.
Intrinsic locks and extrinsic locks have the same mechanism inside for locking, so the performance improvement is purely subjective. It depends on the use cases we discussed above. Extrinsic locks give a more explicit control mechanism for better handling of deadlocks, starvation, and so on.
Nearly all the classes in the java.util.concurrent package use atomic variables instead of synchronization, either directly or indirectly. Classes like ConcurrentLinkedQueue use atomic variables to directly implement wait-free algorithms, and classes like ConcurrentHashMap use ReentrantLock for locking where needed. ReentrantLock, in turn, uses atomic variables to maintain the queue of threads waiting for the lock.
There are different types of DS. Most commonly used are Arrays, List (Dynamic Arrays), Hashes (uses hashing algorithm), Trees (Binary Search Tree, Red-Black Tree), Graph (arrangement of edges and vertices).