JCIP
JCIP
1 ThreadSafety
-----------A class is thread safe if it behaves correctly when accessed from mul
tiple threads, regardless of the scheduling or
interleaving of the execution of those threads by the runtime environment, and w
ith no additional synchronization or
other coordination on the part of the calling code .
Stateless servlets are threadsafe.
2.2 Atomicity
=============
Lost Updates
++ increment - is a read modify write operation.
Lazy initialization - is Check then Act operation
2.2.1 Race Condition
-------------------A race condition occurs when the correctness of a computation depends on the rel
ative timing or interleaving of multiple threads by the runtime.
Correctness depends on the lucky timing.
The most common type of race condition is check then act, where a potentially st
ale observation is used to make a decision on what to do next .
2.2.2 Ex. of Race Condition
---------------------------Lazy Initialization
@NotThreadSafe
public class LazyInitRace {
private ExpensiveObject instance = null;
public ExpensiveObject getInstance() {
if (instance == null)
instance = new ExpensiveObject();
return instance;
}
}
2.2.3 Compound Actions
----------------------Operations A and B are atomic with respect to each other if, from t
he perspective of a thread executing A, when
another thread executes B, either all of B has executed or none of it has.
To ensure thread safety, check then act operations (like lazy initializa
tion) and read modify write
operations (like increment) must always be atomic .
The java.lang.util.concurrent.atomic package contains atomic variable classes fo
r atomic state transition on numbers and object references.
@ThreadSafe
public class CountingFactorizer implements Servlet {
private final AtomicLong count = new AtomicLong(0);
public long getCount() { return count.get(); }
public void service(ServletRequest req, ServletResponse resp) {
BigInteger i = extractFromRequest(req);
BigInteger[] factors = factor(i);
count.incrementAndGet();
encodeIntoResponse(resp, factors);
}
}
2.3 Locking
============
Servlet that Attempts to Cache its Last Result without Adequate Atomicity.
@NotThreadSafe
public class UnsafeCachingFactorizer extends GenericServlet implements Servlet {
private final AtomicReference<BigInteger> lastNumber
= new AtomicReference<BigInteger>();
private final AtomicReference<BigInteger[]> lastFactors
= new AtomicReference<BigInteger[]>();
public void service(ServletRequest req, ServletResponse resp) {
BigInteger i = extractFromRequest(req);
if (i.equals(lastNumber.get()))
encodeIntoResponse(resp, lastFactors.get());
else {
BigInteger[] factors = factor(i);
lastNumber.set(i);
lastFactors.set(factors);
encodeIntoResponse(resp, factors);
}
}
void encodeIntoResponse(ServletResponse resp, BigInteger[] factors) {
}
BigInteger extractFromRequest(ServletRequest req) {
return new BigInteger("7");
}
BigInteger[] factor(BigInteger i) {
// Doesn't really factor
return new BigInteger[]{i};
}
}
Even though the atomic references are individually thread safe,
UnsafeCachingFactorizer has race conditions that could make it produce the wrong
answer .
Using atomic references, we cannot update both lastNumber and lastFactors s
imultaneously, even though each call to set is atomic;
2.3.1 Intrinsic Locks
--------------------synchronized (lock) {
// Access or modify shared state guarded by lock
}
Every Java object can implicitly act as a lock for purposes of synchronization;
these built in locks are called intrinsic locks
or monitor locks .The lock is automatically acquired by the executing thread bef
ore entering a synchronized block and
automatically released when control exits the synchronized block, whether by the
normal control path or by throwing
an exception out of the block .
(Static synchronized methods use the Class object for the lock.)
Listing 2.6 .Servlet that Caches Last Result, But with Unacceptably Poor Concurr
ency
@ThreadSafe
public class SynchronizedFactorizer extends GenericServlet implements Servlet {
@GuardedBy("this") private BigInteger lastNumber;
@GuardedBy("this") private BigInteger[] lastFactors;
public synchronized void service(ServletRequest req,
ServletResponse resp) {
BigInteger i = extractFromRequest(req);
if (i.equals(lastNumber))
encodeIntoResponse(resp, lastFactors);
else {
BigInteger[] factors = factor(i);
lastNumber = i;
lastFactors = factors;
encodeIntoResponse(resp, factors);
}
}
.
2.3.2 Reentrant Locks
--------------------Reentrancy means that locks are acquired on a per thread rather than
per invocation basis.
But if intrinsic locks were not reentrant, the call to super.doSomething would n
ever be able to acquire the lock because
it would be considered already held
2.4 Guarding State with Locks
----------------------------locks enable serialized access to the code paths.
guaranteeing exclusive access to shared state .
Lock vs Volatile
----------------Lock provides both Visibility and atomicity.
Volatile provides both atomicity.
3.5.3 Safely publishing object
-----------------------------To publish an object safely, both the reference to the object and th
e object's state must be made visible to other
threads at the same time .A properly constructed object can be safely published
by:
Initializing an object reference from a static initializer;
public static Holder holder = new Holder(42);
Storing a reference to it into a volatile field or AtomicReference;
Storing a reference to it into a final field of a properly constructed object;
or
Storing a reference to it into a field that is properly guarded by a lock .
Safely published effectively immutable objects can be used safely by any thread
without additional synchronization.
3.5.4 Safe Object Sharing Policies
-----------------------------------The most useful policies for using and sharing objects in a concurrent program a
re:
Thread confined.
A thread confined object is owned exclusively by and confined to
one thread, and can be modified by
its owning thread .
Shared read only
A shared read only object can be accessed concurrently b
y multiple threads without additional
synchronization, but cannot be modified by any thread . S
hared read only objects include immutable
and effectively immutable objects .
Shared thread safe
A thread safe object performs synchronization internally, so
multiple threads can freely access it
through its public interface without further synchronization .
Guarded
A guarded object can be accessed only with a specific
lock held . Guarded objects include
those that are encapsulated within other thread safe objects a
nd published objects that are known to be guarded by a specific lock .
4.Composing Objects
====================
4.1 Designing Thread Safe Objects
--------------------------------The design process for a thread safe class
1. Identify the variables that form the object's state;
2. Identify the invariants that constrain the state variables;
3. Establish a policy for managing concurrent access to the object's state.
5.Building Blocks
=================
5.1.1 Synchronized Oollections
---------------------------Vector, HashTable and Synchronized wrapper classes by Collections.synchronizedXX
X() released in JDK 1.2.
Althouth Synchronized collections are thread safe , but compound actions will re
sult in correctness failure.
The unreliable iteration can be avoided by Client Side locking i.e Lock while it
erating the collection but with addtional cost to scalability.
An alerternative to client side locking is to clone the collection and iterate t
he copy instead.
5.1.2 Iterators and Concurrent Modification
----------------------------------------Iterators are fail fast meaning that if they detect that the collection has ch
anged since iteration began, they throw the
unchecked ConcurrentModificationException
ConcurrentModificationException can arise in single threaded code as well; this
happens when objects are removed from the collection directly
rather than through Iterator.remove.