In Java you can
mark a method or a block of code as synchronized. Synchronized blocks can be
used to avoid race conditions.
Here is a
synchronized method:And here is a
synchronized block of code inside an unsynchronized add method:
public void add(int value) {
synchronized (this) {
this.count +=
value;
}
}
Notice how the
synchronized construct takes an object in parantheses. In the example
"this" is used, which is the instance the add method is called on.
The object taken in the parantheses by the synchronized construct is called a
monitor object. The code is said to be synchronized on the monitor object. A
synchronized method uses the object it belongs to as monitor object. A static
method uses the class object of the class the method belongs.
Only one thread
can execute inside a code block synchronized on the same monitor object.
The following
two examples are both synchronized on the instance they are called on. They are
therefore equivalent with respect to synchronization:
public class MyClass {
public synchronized void log1(String msg1,
String msg2) {
log.writeln(msg1);
log.writeln(msg2);
}
public void log2(String msg1,
String msg2) {
synchronized (this) {
log.writeln(msg1);
log.writeln(msg2);
}
}
}
Here are the
same two examples as static methods, synchronized on the class object of the
class the methods belong to:
public class MyClass {
public static synchronized void log1(String msg1,
String msg2) {
log.writeln(msg1);
log.writeln(msg2);
}
public static void log2(String msg1,
String msg2) {
synchronized (MyClass.class) {
log.writeln(msg1);
log.writeln(msg2);
}
}
}
Here is an
example that starts 2 threads and have both of them call the add method on the
same instance of Counter. Only one thread at a time will be able to call the
add method on the same instance, because the method is synchronized on the
instance it belongs to.
public class Counter {
long count = 0;
public synchronized void add(long value) {
this.count += value;
}
}
public class CounterThread extends Thread {
protected Counter counter = null;
public
CounterThread(Counter counter) {
this.counter = counter;
}
public void run() {
for (int i = 0; i < 10;
i++) {
counter.add(i);
}
}
}
public class Example {
public static void main(String[]
args) {
Counter
counter = new Counter();
Thread
threadA = new CounterThread(counter);
Thread
threadB = new CounterThread(counter);
threadA.start();
threadB.start();
}
}
Two threads
thread are created. The same Counter instance is passed to both of them in
their constructor. The Counter.add() method is synchronized on the instance,
because the add method is an instance method, and marked as synchronized.
Therefore only one of the threads can call the add() method at a time. The
other thread will wait until the first thread leaves the add() method, before
it can execute the method itself.
If the two
threads had referenced two separate Counter instances, there would have been no
problems calling the add() methods simultanously. The calls would have been to
different objects, so the methods called would also be synchronized on
different objects (the object owning the method). Therefore the calls would not
block. Here is how that could look:
public class Example {
public static void main(String[]
args) {
Counter
counterA = new Counter();
Counter
counterB = new Counter();
Thread
threadA = new CounterThread(counterA);
Thread
threadB = new CounterThread(counterB);
threadA.start();
threadB.start();
}
}
Notice how the
two threads, threadA and threadB, no longer reference the same counter
instance.
No comments:
Post a Comment