前言
synchronized是Java的关键字,我们常将其称为同步锁;
- 当修饰普通成员方法时为 “对象锁”
- 当修饰静态成员方法时为 “类锁”
由名称我们很容易认为,如果某代码块获得 “类锁” 之后,那么所有执行到该类同步方法的其他线程都将进入阻塞状态,其实并不会这样。
实验
为探究Java多线程 “类锁” 和 “对象锁” 的区别,笔者做了如下实验:
测试一
public class Test816 {
//同步静态方法:执行时获取 “类锁”
public static synchronized void printNumA() {
int i = 0;
while (true) {
System.out.println("A" + i++);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//同步方法:执行时获取 “对象锁”
public synchronized void printNumB() {
int i = 0;
while (true) {
System.out.println("B" + i++);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//mian方法
public static void main(String[] args) {
new Thread(() -> {
printNumA();
}).start();
new Thread(() -> {
new Test816().printNumB();
}).start();
}
}
运行结果
由上图得知,两线程之间并没有互相阻塞。
测试二
将普通成员方法内部代码使用 “类锁” 加锁
public class Test816 {
public static synchronized void printNumA() {
int i = 0;
while (true) {
System.out.println("A" + i++);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void printNumB() {
int i = 0;
synchronized (Test816.class) {
while (true) {
System.out.println("B" + i++);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
new Thread(() -> {
printNumA();
}).start();
new Thread(() -> {
new Test816().printNumB();
}).start();
}
}
运行结果
其中一个线程获取 “类锁” 之后,另一个线程进入阻塞态。
原因总结
没有真正意义上的类锁,类锁的锁对象实际上为该类的 class对象,而对象锁的锁对象为调用该同步方法的对应实例对象,这两个对象的内存地址明显不同,所以其占有的锁资源不同。