为什么Synchronized block要使用this作同步
学Java并发编程时遇到过一个问题:为什么凡是涉及到Synchronized block的代码,总是使用synchronized(this)这样的代码。一开始很疑惑不解,既然Synchronized block可以通过获得对象的锁来使得多个线程对同一对象的访问同步,那么Synchronized block可以使用任何对象(比如成员变量)来作同步,而为什么偏偏使用this来做同步呢?后来经过一番思索想同了其中的奥妙:如果使用其他对象来做同步,会产生线程不安全。先看如下一段代码:
package concurrent;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
class Testee {
private Integer i = new Integer(1);
public void f() {
synchronized (i) {
System.out.println(Thread.currentThread().getName() + " get i = "
+ i);
System.out.println(Thread.currentThread().getName()
+ " change i = new Integer(2)");
i = new Integer(2);
try {
System.out.println(Thread.currentThread().getName()
+ " Sleep 2 sec");
TimeUnit.SECONDS.sleep(2); // 保证Thread#2获得新的i的锁
System.out.println(Thread.currentThread().getName()
+ " Sleep finished");
System.out.println(Thread.currentThread().getName()
+ " get i = " + i + " again");
} catch (InterruptedException e) {
}
}
}
public void g() {
// 先睡1秒,保证Thread#1完成对i的重新赋值工作
try {
TimeUnit.SECONDS.sleep(1);
synchronized (i) {
System.out.println(Thread.currentThread().getName()
+ " get i = " + i);
System.out.println(Thread.currentThread().getName()
+ " Sleep 2 sec");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName()
+ " Sleep finished");
}
} catch (InterruptedException e) {
}
}
}
public class CriticalSectionTest {
public static void main(String[] args) {
final Testee testee = new Testee();
Thread t1 = new Thread("Thread#1") {
public void run() {
testee.f();
}
};
Thread t2 = new Thread("Thread#2") {
public void run() {
testee.g();
}
};
ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(t1);
exec.execute(t2);
exec.shutdown();
}
}
Output:
pool-1-thread-1 get i = 1 pool-1-thread-1 change i = new Integer(2) pool-1-thread-1 Sleep 2 sec pool-1-thread-2 get i = 2 pool-1-thread-2 Sleep 2 sec pool-1-thread-1 Sleep finished pool-1-thread-1 get i = 2 again pool-1-thread-2 Sleep finished |
所以,在很多使用synchronized block的代码中,都使用this来作同步,因为this是不可重新赋值的,也就避免了上面例子的问题。