java语言规范中有这么一句话,synchronized方法在调用时会自动执行锁定动作,它的方法体在该锁定动作完成之前,是不会被执行的。如果该方法时实例方法,那么它会锁定该方法的实例(即该方法体执行期间的this对象)相关联的监视器。如果该方法时static的,那么它会锁定与表示定义该方法的类的class对象关联的监视器。如果该方法执行结束,无论是正常结束还是猝然结束,都会在相同的监视器上执行解锁操作。
那么我们来验证一下。
public class TestLockObjectInstance {
public static void main(String[] args) throws InterruptedException {
LockObject object = new LockObject();
ThreadA a = new ThreadA(object);
ThreadB b = new ThreadB(object);
a.start();
b.start();
//等待AB执行完
a.join();
b.join();
System.out.println(object.a);
}
public static class ThreadA extends Thread {
LockObject o = null;
ThreadA(LockObject o){
this.o = o;
}
@Override
public void run() {
int i = 0;
for(;i<100000000;i++){
o.test4();
}
}
}
public static class ThreadB extends Thread {
LockObject o = null;
ThreadB(LockObject o){
this.o = o;
}
@Override
public void run() {
int i = 0;
for(;i<100000000;i++){
o.test5();
}
System.out.println("ThreadB i=-------"+i);
}
}
public static class LockObject {
public Integer a = 0;
//上锁
synchronized void test4(){
++a;
}
//不上锁
void test5(){
++a;
}
}
}
执行几次,最终输出如下:
181805610
171202705
191603900
可以看到,此处对变量a的操作并不是线程安全的。
现在,我对test5加锁,假如两个方法锁的是同一个对象,那么即使并发执行,对a的操作也应该是线程安全的。
package concurrent;
public class TestLockObjectInstance {
public static void main(String[] args) throws InterruptedException {
LockObject object = new LockObject();
ThreadA a = new ThreadA(object);
ThreadB b = new ThreadB(object);
a.start();
b.start();
//等待AB执行完
a.join();
b.join();
System.out.println(object.a);
}
public static class ThreadA extends Thread {
LockObject o = null;
ThreadA(LockObject o){
this.o = o;
}
@Override
public void run() {
int i = 0;
for(;i<100000000;i++){
o.test4();
}
}
}
public static class ThreadB extends Thread {
LockObject o = null;
ThreadB(LockObject o){
this.o = o;
}
@Override
public void run() {
int i = 0;
for(;i<100000000;i++){
o.test5();
}
System.out.println("ThreadB i=-------"+i);
}
}
public static class LockObject {
public Integer a = 0;
synchronized void test4(){
++a;
}
synchronized void test5(){
++a;
}
}
}
发现无论执行多少次,输出都是200000000。证明这是线程安全的操作!
200000000
200000000
200000000
注意,锁的是当前调用方法的实例,即LockObject的实例,而不是TestLockObjectInstance的实例!