Saturday, July 31, 2010

Threads - Thread Synchronization

* every instance of class Object and its subclass's has a lock
* primitive data type fields (Scalar fields) can only be locked via their enclosing class
* fields cannot be marked as synchronized however they can be declared volatile which orders the way they can be used or you can write synchronized accessor methods
* array objects can be synchronized BUT their elements cannot, nor can their elements be declared volatile
* Class instances are Objects and can be synchronized via static synchronized methods

Synchronized blocks

* allow you to execute synchronized code that locks an object without requiring you to invoke a synchronized method

synchronized( expr ) {
// 'expr' must evaluate to an Object
}

Synchronized methods

* declaring a method as synchronized ie synchronized void f() is equivalent to

void f() { synchronized(this) {
// body of method
}
}

* the synchronized keyword is NOT considered part of a method's signature. IT IS NOT AUTOMATICALLY INHERITED when subclasses override superclass methods
* methods in Interfaces CANNOT be declared synchronized
* constructors CANNOT be declared synchronized however they can contain synchronized blocks
* synchronized methods in subclasses use the same locks as their superclasses
* synchronization of an Inner Class is independent on it's outer class
* a non-static inner class method can lock it's containing class by using a synchronized block

synchronized(OuterClass.this) {
// body
}

Locking

* locking follows a built-in acquire-release protocol controlled by the synchronized keyword
* a lock is acquired on entry to a synchronized method or block and released on exit, even if the exit is the result of an exception
* you cannot forget to release a lock
* locks operate on a per thread basis, not on a per-invocation basis
* Java uses re-entrant locks ie a thread cannot lock on itself

class Reentrant {

public synchronized void a() {
b();
System.out.println("here I am, in a()");
}
public synchronized void b() {
System.out.println("here I am, in b()");
}
}

* in the above code, the synchronized method a(), when executed, obtains a lock on it's own object. It then calls synchronized method b() which also needs to acquire a lock on it's own object
* if Java did not allow a thread to reacquire it's own lock method b() would be unable to proceed until method a() completed and released the lock; and method a() would be unable to complete until method b() completed. Result: deadlock
* as Java does allow reentrant locks, the code compiles and runs without a problem

* the locking protocol is only followed for synchronized methods, it DOES NOT prevent unsynchronized methods from accessing the object
* once a thread releases a lock, another thread may acquire it BUT there is no guarantee as to WHICH thread will acquire the lock next

Class fields and methods

* locking an object does not automatically protect access to static fields
* protecting static fields requires a synchronized static block or method
* static synchronized statements obtain a lock on the Class vs an instance of the class
* a synchronized instance method can obtain a lock on the class

synchronized(ClassName.class) {
// body
}

* the static lock on a class is not related to any other class including it's superclasses
* a lock on a static method has no effect on any instances of that class (JPL pg 185)
* you cannot effectively protect static fields in a superclass by adding a new static synchronized method in a subclass; an explicit block synchronization is the preferred way
* nor should you use synchronized(getClass()); this locks the actual Class which might be different from the class in which the static fields are declared

No comments: