Synchronized的误解
昨日终于对synchronized这个关键字有了更进一步的理解,记录一下,方便日后查询。
假设我有一个这样的类:
class MultiThreadTest implements Runnable {
int myId;
public MultiThreadTest(int id) {
myId = id;
}
@Override
public synchronized void run() {
for (int i = 0; i < 3; i++) {
System.out.println("myId is " + i + " : " + Thread.currentThread().getName());
}
}
}
main函数如下:
public static void main(String[] args) {
new Thread(new MultiThreadTest(1)).start();
new Thread(new MultiThreadTest(2)).start();
new Thread(new MultiThreadTest(3)).start();
}
在main函数中,我使用三个thread调用它,因为synchronized关键字的关系,最后的理想结果应该是
myId is 0 : Thread-1
myId is 1 : Thread-1
myId is 2 : Thread-1
myId is 0 : Thread-0
myId is 1 : Thread-0
myId is 2 : Thread-0
myId is 0 : Thread-2
myId is 1 : Thread-2
myId is 2 : Thread-2
即所有的thread应该是锁定直至自身运行结束为止,才运行下一个
但是实际结果却是这个样子
myId is 0 : Thread-2
myId is 0 : Thread-0
myId is 0 : Thread-1
myId is 1 : Thread-2
myId is 1 : Thread-0
myId is 2 : Thread-0
myId is 2 : Thread-2
myId is 1 : Thread-1
myId is 2 : Thread-1
很显然,各个线程之间的运行还是乱的,synchronized并没有起到任何作用。
为什么会这样?
原因在于main调用thread是,实际上是new了 3 个 instances,这样一来,java就将之视为3个独立的程序(大概是这个意思,但是用词不准确)
我们现在来看看java对于synchronized的相关描述
synchronized has two effects:
- First, it is not possible for two invocations of synchronized methods on the same object to interleave. When one thread is executing a synchronized method for an object, all other threads that invoke synchronized methods for the same object block (suspend execution) until the first thread is done with the object.
- Second, when a synchronized method exits, it automatically establishes a happens-before relationship with any subsequent invocation of a synchronized method for the same object. This guarantees that changes to the stat e of the object are visible to all threads.
注意,这里面的关键字是the same object,也就是说,synchronized的作用只针对同一个object或者说instance有效。而上面的main中,却一下子new了3个instance,对于java来说,相当于3个不同的object,而这三个objects分别只运行了一次,所以synchronized在这里面就毫无意义了,就算将run()函数前的synchronized去掉,结果也没有区别。
如何得到理想结果呢?
其实上面已经告诉我们答案了,就是the same object,无论你有多少个thread,只要调用的都是同一个instance,那么这个synchronized就会起作用。
如果main函数改成这样
public static void main(String[] args) {
Runnable r = new MultiThreadTest();
new Thread(r).start();
new Thread(r).start();
new Thread(r).start();
}
就能得到理想结果了。