学习笔记day12 synchronized底层实现及锁升级机制

原博客:https://blog.csdn.net/weixin_40394952/article/details/118693945

一、synchronized使用方法

1、修饰实例方法,对当前实例对象this加锁

2、修饰静态方法,对当前类的class对象加锁

3、修饰代码块,指定加锁对象,对给定对象加锁

代码示例:

package cn.mrhan.java.thread.synchronize;

/**
 * @Author hanYu
 * @Date 2021/8/24
 * @Description
 **/
public class SyncTest {
    public static void main(String[] args) {
        Thread A = new Thread(()->Service.m1());
        Thread B = new Thread(()->Service.m2());
        A.start();
        B.start();
        Service service = new Service();
        Thread C = new Thread(()->service.m3());
        Thread D = new Thread(()->service.m4());
        C.start();
        D.start();
    }
    private static class Service{
        /**
         * m1方法synchronized修饰静态方法,锁定的是Service.Class
         * @throws InterruptedException
         */
        public synchronized static void m1() {
            System.out.println("m1 getLock");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("m1 releaseLock");
        }

        /**
         * m2方法synchronized修饰静态方法,锁订单额是Service
         * 当线程AB同时启动,输出的依次是m1 getLock,m1 releaseLock,m2 getLock,m2 releaseLock
         * 可以说明m1和m2方法是同一把锁 当线程A释放锁后,线程2才会调用M2方法
         */
        public synchronized static void m2(){
            System.out.println("m2 getLock");
            System.out.println("m2 releaseLock");
        }

        /**
         * m3方法synchronized修饰实例方法,修饰的是调用此方法的实例对象
         */
        public synchronized void m3(){
            System.out.println("m3 getLock");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("m3 releaseLock");
        }

        /**
         * 修饰同步块 修饰括号里指定的对象
         */
        public void m4(){
            synchronized (this){
                System.out.println("m4 getLock");
                System.out.println("m4 releaseLock");
            }
        }


    }

}

二、synchronized实现原理

1、java对象组成

1)对象头: 标记字段(用于存储对象自身运行时数据),类型指针(对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例)

2)实例变量:存放类的属性数据信息

3)填充数据:不是必须存在 仅仅是为了字节对齐

2、原理

为什么我们在使用synchronized的时候不用释放锁,jvm底层通过monitorexit关键字帮我们做了锁释放的工作

Synchronized底层通过⼀个monitor的对象来完成,每个对象有⼀个监视器锁(monitor)。当monitor被占⽤时就会处于锁定状态,线程执⾏monitorenter指令时尝 试获取monitor的所有权

1)如果monitor的进⼊数为0,则该线程进⼊monitor,然后将进⼊数设置为1,该线程即为monitor的所有者。

(2)如果线程已经占有该monitor,只是重新进⼊,则进⼊monitor的进⼊数加1。

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

执⾏monitorexit的线程必须是object所对应的monitor的所有者。指令执⾏时,monitor的进⼊数减1,如果减1 后进⼊数为0,那线程退出monitor,不再是这个monitor的所有者。其他被这个monitor阻塞的线程可以尝试去获 取这个monitor的所有权

三、锁升级过程

Jdk1.5之前⽤户需要跟内核态申请锁,然后内核态还会给⽤户态。这个过程是⾮常消耗时间的,导致锁效率特别低。把jvm就可以完成的锁操作拉取出来提升 效率,所以也就有了锁优化。锁优化后的升级过程为:无锁——>偏向锁——>轻量级锁——>重量级锁

1)偏向锁:

⽇常⽤的synchronized锁过程中70%-80%的情况下,⼀般都只有⼀ 个线程去拿锁,例如我们常使⽤的System.out.println、StringBuffer,虽然底层加了syn锁,但是基本没有多线程 竞争的情况。那么这种情况下,没有必要升级到轻量级锁级别了

2)轻量锁:轻量级锁是由偏向锁升级而来,当存在第二个线程申请同一个锁对象时,偏向锁就会立即升级为轻量级锁。注意这里的第二个线程只是申请锁,不存在两个线程同时竞争锁,可以是一前一后地交替执行同步块

3)自旋锁和自适应自旋锁:

当有多个线程同时竞争锁时,会有一个自旋的过程,即让线程循环一直尝试去获取锁,而自适应自旋锁则是自动根据上一次获取到锁的时间调整自旋的时间

自旋锁的意义在于:当持有锁的线程释放了锁,当前线程才可以再去竞争锁,但是如果按照这样的规则,就会浪费大量的性能在阻塞和唤醒的切换上,特别是线程占用锁的时间很短的话。为了避免阻塞和唤醒的切换,在没有获得锁的时候就不进入阻塞,而是不断地循环检测锁是否被释放,这就是自旋。在占用锁的时间短的情况下,自旋锁表现的性能是很高的。

4) 重量锁:

如果自旋获取锁也失败了,则升级为重量级锁,也就是把线程阻塞起来,等待唤醒。其利用操作系统底层的同步机制去实现Java中的线程同步。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值