Hotspot synchronized与volatile关键字实现(一) 源码解析

 目录

一、synchronized用法

1、修饰实例方法

2、修饰静态方法

3、修饰代码块

二、synchronized底层实现

1、修改代码块的字节码分析

2、monitorenter 指令实现

3、InterpreterRuntime::monitorenter

4、monitorexit 指令实现

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
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值