目录
3、InterpreterRuntime::monitorenter
5、InterpreterRuntime::monitorexit
6、monitorenter/ monitorexit 指令的编译执行
本篇博客来从字节码实现层面详细探讨synchronized与volatile关键字的实现细节。
一、synchronized用法
1、修饰实例方法
修饰实例方法时,执行该方法时必须获取某个实例关联的锁,即并发调用同一实例的多个被修饰的实例方法时只能执行其中的某一个,测试用例如下:
public class AddTest {
private int a;
synchronized int add(){
a++;
return a;
}
synchronized int add2(){
a+=2;
return a;
}
}
private final long NUM=100000000;
@Test
public void test() throws Exception {
AddTest a=new AddTest();
Thread t=new Thread(new Runnable() {
@Override
public void run() {
long startTime= System.currentTimeMillis();
for(int i=0;i<NUM;i++) {
a.add();
}
long time=System.currentTimeMillis()-startTime;
System.out.println("t run end,time->"+time);
}
});
t.start();
Thread t2=new Thread(new Runnable() {
@Override
public void run() {
long startTime= System.currentTimeMillis();
for(int i=0;i<NUM;i++) {
a.add2();
}
long time=System.currentTimeMillis()-startTime;
System.out.println("t2 run end,time->"+time);
}
});
t2.start();
t.join();
t2.join();
System.out.println("job end");
}
@Test
public void test2() throws Exception {
Thread t=new Thread(new Runnable() {
@Override
public void run() {
AddTest a=new AddTest();
long startTime= System.currentTimeMillis();
for(int i=0;i<NUM;i++) {
a.add();
}
long time=System.currentTimeMillis()-startTime;
System.out.println("t run end,time->"+time);
}
});
t.start();
Thread t2=new Thread(new Runnable() {
@Override
public void run() {
AddTest a=new AddTest();
long startTime= System.currentTimeMillis();
for(int i=0;i<NUM;i++) {
a.add2();
}
long time=System.currentTimeMillis()-startTime;
System.out.println("t2 run end,time->"+time);
}
});
t2.start();
t.join();
t2.join();
System.out.println("job end");
}
第一个测试用例就是并发调用同一个实例的两个被修饰的实例方法,其执行结果如下:
第二个测试用例是并发调用不同实例的两个被修饰的实例方法,其执行结果如下:
第一个测试用例因为两个方法在同一时间只能执行其中一个,而第二个测试用例是两个方法在并行执行互不影响,因此第一个的耗时是第二个的两倍多,超过两倍的耗时是获取锁释放锁等同步操作的损耗。
2、修饰静态方法
修饰静态方法时,执行该方法时必须获取该类的class如String.class实例关联的锁,即同一时间多个不同实例并发调用不同的静态方法时只能执行其中的一个方法,测试用例如下:
public class AddTest {
private int a;
private static int b;
synchronized int add(){
a++;
return a;
}
synchronized int add2(){
a+=2;
return a;
}
synchronized static int add3(){
b++;
return b;
}
synchronized static int add4(){
b+=2;
return b;
}
}
@Test
public void test2() throws Exception {
Thread t=new Thread(new Runnable() {
@Override
public void run() {
AddTest a=new AddTest();
long startTime= System.currentTimeMillis();
for(int i=0;i<NUM;i++) {
a.add();
}
long time=System.currentTimeMillis()-startTime;
System.out.println("t run end,time->"+time);
}
});
t.start();
Thread t2=new Thread(new Runnable() {
@Override
public void run() {
AddTest a=new AddTest();
long startTime= System.currentTimeMillis();
for(int i=0;i<NUM;i++) {
a.add2();
}
long time=System.currentTimeMillis()-startTime;
System.out.println("t2 run end,time->"+time);
}
});
t2.start();
t.join();
t2.join();
System.out.println("job end");
}
@Test
public void test3() throws Exception {
Thread t=new Thread(new Runnable() {
@Override
public void run() {
AddTest a=new AddTest();
long startTime= System.currentTimeMillis();
for(int i=0;i<NUM;i++) {
a.add3();
}
long time=System.currentTimeMillis()-startTime;
System.out.println("t run end,time->"+time);
}
});
t.start();
Thread t2=new Thread(new Runnable() {
@Override
public void run() {
AddTest a=new AddTest();
long startTime= System.currentTimeMillis();
for(int i=0;i<NUM;i++) {
a.add4();
}
long time=System.currentTimeMillis()-startTime;
System.out.println("t2 run end,time->"+time);
}
});
t2.start();
t.join();
t2.join();
System.out.println("job end");
}
test2的耗时不变,如下图:
test3的耗时跟test1差不过了,如下图:
这是因为他们都是同一时间只能执行其中的某一个方法,不同的是test1是获取某个实例关联的锁,test3是获取类的class实例关联的锁。
3、修饰代码块
修饰代码块时需要指定在哪个实例上同步,如果该实例是某个类的class实例或者静态属性时,则同样的,该类的多个不同实例并发调用该代码块时同一时间只能其中一个能执行;如果该实例是某个实例属性或者this时,则同样的,并发调用同一实例的该代码块时只能执行其中一个;如果该实例是局部变量,因为局部变量是执行该方法时私有的,其他线程不会访问到该变量,也就不会去获取该实例关联的锁了,所以实际相当于没有加锁,JVM优化时会将该锁自动消除。
测试用例如下:
public class AddTest {
private int a;
private static int b;
int add(){
synchronized (this) {
a++;
}
return a;
}
int add2(){
synchronized (this) {
a+=2;
}
return a;
}
static int add3(){
synchronized (AddTest.class) {
b++;
}
return b;
}
static int add4(){
synchronized (AddTest.class) {
b+=2;
}
return b;
}
}
public class AddTest {
private int a;
private s