In Java, thread synchronization is achieved through two constructs: synhcronized
methods and synhcronized statements.
int a=7;
void synchronized commonMethod(){
a+=5;
//
additional processing of data not shared among threads // but
unnecessarilly synchronized
}
An example of synchronized methods can be seen above.
int a=7;
void commonMethod(){
synchronized(this){
a+=5;
}
// additional processing of data
not shared among threads.
// Unnecessary synchronization is avoided.
}
Code fragment above is an example of synchronized statements. Note that
synchronized methods may lead to unnecessary synchronization of code segments,
while synchronized statements has the flexibility to avoid those situations.
Each object has an internal entity called intrinsic lock, also called
monitor lock. A thread which wants exclusive access to object’s resources, in
our case instance variables, has to lock
object by acquiring object’s intrinsic
lock.
Once a synchronized method or synchronized statement is reached thread
tries to acquire the lock of the object realated to that statement/method. If
it can acquire the lock it continues its
execution, threads coming afterwards won’t let inside that statement/method
until first thread finishes its job and releases lock. If the lock is already
owned by some other thread, newly arrived thread will be blocked until it has chance to acquire lock.
Synronized structs are not only about limiting access to single thread. But
they also create memory barries. That is to say, when a thread acquires lock
its local momory is invalidated and updated with up-to-date values from main
memory. When a lock is released modified values are written to main memory.
This way synchronization constructs ensures that all threads synchronizing on
the same monitor, see the same memory space.
Note That : When synchronized method is used on an instance
method, lock is acquired on current
object instance. When used on static methods lock is acquired on class itself.
Same effect can be gained by using synchronized statement and specifying class
as argument.
Note That : Once a thread enters a synchronized section no
other threads are allowed inside. On the other hand, same thread can enter
other syncronized sections of the same object. It can invoke nested
synchronized methods, for example. This is called reentrant synchronization.
Note That : Read/Write operations on reference variables
and primitive variables other than long and double, Read/Write operations on
all volatile variables are atomic.
Altough read/write operations for non-volatile variables are safe from thread interference, they are prone to
memory consistency errors.
Volatile Variables : For volatile variables each read and write is synchronized
with main memory and immediately visible to other threads. Order of volatile
variables are not changed with other volatile or non-volatile variables. Even
more write to volatile variables acst like a lock relaese so that all modified variables
in local cache are syncrhonized with main memory. Read on volatile varibales
act like a lock acquire, so that local
cahche is reloaded from main memory with fresh values. To sum up, in memory
aspect, access to volatile variables acts like synchronized statements.
Consider the example which was used to illustrate memory consistency errors.
This time non-volatile field is made volatile:
volatile int a=7;
boolean done=false;
void increment(){
a=+5;
done=true;
}
int get(){
if(done){
return a;
}
return -1;//indicating result
is not ready yet
}
Keeping in mind that order of volatile variables remains unchanged and
read/write operations are syncronized with main memory together with other
variables, reconsider the flow. Thread 1, calling increment method, will always
execute increment statement before setting done flage. Thread 2 will always be
sure that if done flag is set field a has calculated up-to-date value.
Final Fields: When an
object is constructed, its final fields are initiliazed and will be visible to
all threads without need for synchronization provided that object reference is
not “leaked” to outside during construction. Consider the following code
listing:
class KeyEventListener extends KeyListener{
final int max;
public KeyEventListener(JFrame frame,int maxValue){
frame.addKeyListener(this); // Avoid This
max=maxValue;
}
}
Statement marked as “Avoid This” makes object reference visible to outside
and final field max becomes accessible to other threads before it is
initiliazed. This kind of operations, which reveleas object reference to outide,
must be done in seperate methods, not in constructors. Example below illsutrates how it should be
done.
class KeyEventListener extends KeyListener{
final int max;
public KeyEventListener(int maxValue){
max=maxValue;
}
public void register(JFrame frame){
frame.addKeyListener(this);//Correct way
}
}
Syhchronized statement or synchronized
mehtod?
Syhcronized methods are good if whole
method block needs synchronization. Because they are more neat and method signature
indiacates that it is thread-safe. In other cases synchronized statements are
preferable. Because they allow more fine grained synronization. With synchronized
statements it is possible to lock on other objects.
Further Reading:
Comments
Post a Comment