java锁升级

 锁升级:

在java当中,因为并发的情况,同一段代码会被多个线程同时执行,造成其中的变量没有达到想要的效果,即非原子性。

为了解决这一个问题,java中提供了有synchronized关键字。

synchronized是依赖jvm内部对象Monitor实现的,通过进入与退出Monitor对象实现方法与代码块同步。监视器锁的实现依赖底层操作系统的Mutex lock(互斥锁)实现的,它的性能较低。同步代码块的synchronized关键字在字节码文件中会被翻译成 monitorenter和monitorexit两条指令来标志同步代码块的开始位置和结束为止。

 

每个对象有一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:

1.monitorenter

1.如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者;

2.如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1;

3.如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权;

2.monitorexit

1.执行monitorexit的线程必须是objectref所对应的monitor的所有者。

2.指令执行时,monitor的进入数减1,如果减1后进入数为0,那线程退出monitor,不再是这个monitor的所有者。其他被这个monitor阻塞的线程可以尝试去获取这个 monitor 的所有权。

 

monitorexit,指令出现了两次,第1次为同步正常退出释放锁;第2次为发生异步退出释放锁

 

通过上面两段描述,我们应该能很清楚的看出Synchronized的实现原理,Synchronized的语义底层是通过一个monitor的对象来完成,其实wait/notify等方法也依赖于monitor对象,这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则会抛出java.lang.IllegalMonitorStateException的异常的原因

 

在同步代码块和同步方法中:

public class SimpleTest {

    public void test01(){
        synchronized (this){
            String s = new String("");
            System.out.println("3333");
        }
    }

    public synchronized void test02(){
        System.out.println("e333");
    }

}

javap -v 获得字节码

同步代码块:monitorenter 和monitorexit

同步方法

flags: ACC_PUBLIC, ACC_SYNCHRONIZED
有标志:ACC_SYNCHRONIZED

 

当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先获取monitor,获取成功之后才能执行方法体,方法执行完后再释放monitor。在方法执行期间,其他任何线程都无法再获得同一个monitor对象。

 

两种同步方式本质上没有区别,只是方法的同步是一种隐式的方式来实现,无需通过字节码来完成。两个指令的执行是JVM通过调用操作系统的互斥原语mutex来实现,被阻塞的线程会被挂起、等待重新调度,会导致“用户态和内核态”两个态之间来回切换,对性能有较大影响。

 

@Test
    public void testFullLockUpgrade() throws InterruptedException {
        //打印当前线程id
        System.out.println("当前线程id:"+Thread.currentThread().getId());
        TimeUnit.SECONDS.sleep(5);//jvm 对偏向锁的延迟开启
        A a = new A();
        System.out.println("----------匿名偏向start-------");
        printHead(a);//匿名偏向状态 00000101 00000000 00000000 00000000 00000000 00000000 00000000 00000000
                         // 倒序后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000101  没有线程id 所以是匿名偏向
        System.out.println("----------匿名偏向end-------");


        //在匿名偏向状态下调用hashcode会变成无锁不可偏向状态
        System.out.println("a的hashcode:"+a.hashCode());
        System.out.println("----------无锁不偏向start-------");
        printHead(a); //00000001 : unused25 + hashcode31+ unused1 + age4 + biased 1 + lock 2
        System.out.println("----------无锁不偏向end-------");

        synchronized (a){
            System.out.println("--------轻量级start-------");
            printHead(a); //11000000 00000111 11100110 00001001 轻量级锁
            System.out.println("--------轻量级end-------");
        }


        CyclicBarrier cyclicBarrier = new CyclicBarrier(2);//模拟并发
        CountDownLatch countDownLatch = new CountDownLatch(2);//等待结束

        new Thread(()->{
            try {
                cyclicBarrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
            synchronized (a){
                System.out.println("----------重量级1-start---------");
                printHead(a); //11000000 00000111 11100110 00001001 轻量级锁
                System.out.println("----------重量级1-end---------");
            }
            countDownLatch.countDown();
        }).start();
        new Thread(()->{
            try {
                cyclicBarrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
            synchronized (a){
                System.out.println("----------重量级2-start---------");
                printHead(a); //11000000 00000111 11100110 00001001 轻量级锁
                System.out.println("----------重量级2-end---------");
            }
            countDownLatch.countDown();
        }).start();

        countDownLatch.await();
        System.out.println("----------全部运行完成-start---------");
        printHead(a); //00000001 无锁不偏向
        System.out.println("----------全部运行完成-end---------");
    }


private void printHead(Object obj){
        System.out.println(ClassLayout.parseInstance(obj).toPrintable());
    }

 

运行结果:

当前线程id:1
----------匿名偏向start-------
# WARNING: Unable to attach Serviceability Agent. You can try again with escalated privileges. Two options: a) use -Djol.tryWithSudo=true to try with sudo; b) echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
com.sewell.sync.A object internals:
 OFFSET  SIZE      TYPE DESCRIPTION                               VALUE
      0     4           (object header)                           05 00 00 00 (00000101 00000000 00000000 00000000) (5)
      4     4           (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4           (object header)                           aa 04 01 f8 (10101010 00000100 00000001 11111000) (-134150998)
     12     1   boolean A.myBoolean                               true
     13     3           (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 3 bytes external = 3 bytes total

----------匿名偏向end-------
a的hashcode:1935637221
----------无锁不偏向start-------
com.sewell.sync.A object internals:
 OFFSET  SIZE      TYPE DESCRIPTION                               VALUE
      0     4           (object header)                           01 e5 7a 5f (00000001 11100101 01111010 01011111) (1601889537)
      4     4           (object header)                           73 00 00 00 (01110011 00000000 00000000 00000000) (115)
      8     4           (object header)                           aa 04 01 f8 (10101010 00000100 00000001 11111000) (-134150998)
     12     1   boolean A.myBoolean                               true
     13     3           (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 3 bytes external = 3 bytes total

----------无锁不偏向end-------
--------轻量级start-------
com.sewell.sync.A object internals:
 OFFSET  SIZE      TYPE DESCRIPTION                               VALUE
      0     4           (object header)                           c0 17 d4 02 (11000000 00010111 11010100 00000010) (47454144)
      4     4           (object header)                           00 70 00 00 (00000000 01110000 00000000 00000000) (28672)
      8     4           (object header)                           aa 04 01 f8 (10101010 00000100 00000001 11111000) (-134150998)
     12     1   boolean A.myBoolean                               true
     13     3           (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 3 bytes external = 3 bytes total

--------轻量级end-------
----------重量级2-start---------
com.sewell.sync.A object internals:
 OFFSET  SIZE      TYPE DESCRIPTION                               VALUE
      0     4           (object header)                           3a d5 80 67 (00111010 11010101 10000000 01100111) (1736496442)
      4     4           (object header)                           b4 7f 00 00 (10110100 01111111 00000000 00000000) (32692)
      8     4           (object header)                           aa 04 01 f8 (10101010 00000100 00000001 11111000) (-134150998)
     12     1   boolean A.myBoolean                               true
     13     3           (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 3 bytes external = 3 bytes total

----------重量级2-end---------
----------重量级1-start---------
com.sewell.sync.A object internals:
 OFFSET  SIZE      TYPE DESCRIPTION                               VALUE
      0     4           (object header)                           3a d5 80 67 (00111010 11010101 10000000 01100111) (1736496442)
      4     4           (object header)                           b4 7f 00 00 (10110100 01111111 00000000 00000000) (32692)
      8     4           (object header)                           aa 04 01 f8 (10101010 00000100 00000001 11111000) (-134150998)
     12     1   boolean A.myBoolean                               true
     13     3           (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 3 bytes external = 3 bytes total

----------重量级1-end---------
----------全部运行完成-start---------
com.sewell.sync.A object internals:
 OFFSET  SIZE      TYPE DESCRIPTION                               VALUE
      0     4           (object header)                           01 e5 7a 5f (00000001 11100101 01111010 01011111) (1601889537)
      4     4           (object header)                           73 00 00 00 (01110011 00000000 00000000 00000000) (115)
      8     4           (object header)                           aa 04 01 f8 (10101010 00000100 00000001 11111000) (-134150998)
     12     1   boolean A.myBoolean                               true
     13     3           (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 3 bytes external = 3 bytes total

----------全部运行完成-end---------

Process finished with exit code 0

 

 

 

 


posted @ 2021-08-31 21:19  sewell_画风  阅读(242)  评论(0)    收藏  举报