上面的例子是Synchronized关键字的使用方式之一,此时,synchronized标记的是类的实例方法,锁对象是类的实例对象。当然还有其他使用方式:
private static synchronized void test() {
for (int i = 0; i < 10; i++) {
try {
TimeUnit.MILLISECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(num++);
}
}
此时,synchronized标记的是类的静态方法,锁对象是类。
以上两种,是直接标记在方法上。
还可以包裹代码块:
private void test() {
synchronized (Main.class) {
for (int i = 0; i < 10; i++) {
try {
TimeUnit.MILLISECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(num++);
}
}
}
此时锁的对象是 类。
private void test() {
synchronized (this) {
for (int i = 0; i < 10; i++) {
try {
TimeUnit.MILLISECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(num++);
}
}
}
此时锁的对象是类的实例对象。
private Object object = new Object();
private void test() {
synchronized (object) {
for (int i = 0; i < 10; i++) {
try {
TimeUnit.MILLISECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(num++);
}
}
}
此时,锁对象是Object的对象。
JConsole探究Synchronized关键字
我们需要用到JDK自带的一个工具:JConsole,它位于JDK的bin目录下。
为了让观察更加方便,我们需要给线程起一个名字,每个线程内sleep的时间稍微长一点:
public class Main {
private synchronized void test() {
try {
TimeUnit.SECONDS.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Main main = new Main();
for (int i = 0; i < 5; i++) {
new Thread(() -> {
main.test();
}, "Hello,Thread " + i).start();
}
}
}
我们先启动项目,然后打开JConsole,找到你项目的进程,就可以连接上去了。
可以看到,5个线程已经显示在JConsole里面了:
点击某个线程,可以看到关于线程的一些信息:
其中四个线程都处于BLOCKED,只有一个处于TIME_WAITING,说明只有一个线程获得了锁,并在TIME_WAITING,其余的线程都没有获得锁,没有进入到方法,说明了Synchronized的互斥性。关于线程的状态,这篇不会深入,以后可能会介绍这方面的知识。
因为我是一边写博客,一边执行各种操作的,所以速度上有些跟不上,导致截图和描述不同,大家可以自己去试试。
javap探究Synchronized关键字
为了把问题简单化,让大家看的清楚,我只保留synchronized相关的代码:
public class Main {
public static void main(String[] args) {
synchronized (Main.class) {
}
}
}
编译后,用javap命令查看字节码文件:
javap -v Main.class
用红圈圈出来的就是添加synchronized后带来的命令了。执行同步代码块,先是调用monitorenter命令,执行完毕后,再调用monitorexit命令,为什么会有两个monitorexit呢,一个是正常执行办法后的monitorexit,一个是发生异常后的monitorexit。