Java Synchronized

转:阿里面试题深度解析:Synchronized(obj){...}是锁对象还是代码块?Why?_哔哩哔哩_bilibili

马士兵说:【一小时】直插Java高并发最深处-AQS_哔哩哔哩_bilibili

{2021最新}java并发编程中最难的AQS框架源码解析-【视频教程全集】_哔哩哔哩_bilibili

CAS是乐观锁的实现,synchronized就是悲观锁了

为什么需要内存对齐呢?寻址最优

问题来了,那么锁的具体是什么?

1. synchronized用在普通类方法上,相当于锁的是调用这个方法的对象

 2. synchronized用在static类方法上,相当于锁的是A.class

 3. 优化

public class A {

    int num = 0;
    AtomicInteger atomicInteger = new AtomicInteger();

    public long getNum(){
        return atomicInteger.get();
    }

    public void incrementNum(){

//        synchronized (this){
//            num++;
//        }
        atomicInteger.incrementAndGet();
    }
}

atomicInteger既可以保证多线程数据正确,又可以提升效率 

同理:LongAdder也可以实现类似效果,同时也会有效率的提升

public class A {

    int num = 0;
    AtomicInteger atomicInteger = new AtomicInteger();

    LongAdder longAdder = new LongAdder();

    public long getNum(){
        return longAdder.longValue();
        //return atomicInteger.get();
    }

    public void incrementNum(){

//        synchronized (this){
//            num++;
//        }
//        atomicInteger.incrementAndGet();

        longAdder.increment();
    }
}

LongAdder内部实现了分段CAS

​​​​​​​

 

0. JDK实现版本

1.6之前【重量级锁:操作系统线程调度,内核态和用户态切换,阻塞、唤醒、抢锁】

那么我们继续来看看,在1.6之前,加锁的对象是什么样子的?

有两个队列:
一个是EntryList 阻塞队列 

另一个调用wait 是同步等待队列

来看看Synchronized的底层实现

可以看到有两个字节码:montiorenter、 monitorexit 是JVM指令码

Java虚拟机执行到这行代码会调用底层具体C++的实现,也就是所有的锁升级都是在HotSpot源码里面实现的,底层有大量的实现

执行monitorenter的时候,会解释成上述C++实现的代码 

UseBiasedLocking:偏向锁是否开启

LongAdder:

其中,在1.6之后,对synchronized做了很多很多优化?

1. 引入了锁的状态

2. 基于原则:对某一块代码加锁(如:++),可能很多时候在线上运行的时候,只有一个线程来运行加锁的代码块

3. 偏向锁【在对象内部写入线程id 】,尤为轻量,不需要若干复杂操作 

1. 了解下Java对象的布局具体是什么样子的?

引入pom依赖

        <dependency>
            <groupId>org.openjdk.jol</groupId>
            <artifactId>jol-core</artifactId>
            <version>0.16</version>
        </dependency>
L l = new L();
System.out.println(ClassLayout.parseInstance(l).toPrintable());

尝试打印对下在内存里面的布局[里面会有hash码,但是hash是地址经过计算的结果,如果没有计算,那么对象内存布局中也不会有的]

 如上:object header是对象头。 alignment gap 是填充内容, 还有一部分是实例数据 

 所以:可以指导java对象头是12字节【64位机器上】,所以总共96bit

java对象是8的倍数对齐

其中对象头里面包含若干信息:gc对象的年龄、哈希码、同步状态synchronized 

64位虚拟机下:mark word下占用bit情况(64bit) 

klass pointer:32bit【另外需要注意:如果这里没有开启指针压缩,那么这里是64bit】

 

如下是:锁的升级过程

@Data
public class User {
    private int age;
    private String name;
}
public class LockUpGrade {
    public static void main(String[] args) throws InterruptedException {

        User userTmp = new User();
        System.out.println("无状态(001):" + ClassLayout.parseInstance(userTmp).toPrintable());
        //jvm默认延时4s开启偏向锁,可通过:-XX:BiasedLockingStartupDelay=0, 取消延时
        //如果不需要偏向锁,可通过-XX:UseBiasedLocking=false 来设置
        Thread.sleep(5000);
        User user = new User();
        System.out.println("启用偏向锁(101):" + ClassLayout.parseInstance(user).toPrintable());

        for (int i=0; i<2; i++){
            synchronized (user){
                System.out.println("偏向锁(101),带id:" + ClassLayout.parseInstance(user).toPrintable());
            }

            //偏向锁释放,对象头里面不会做任何修改。偏向锁是偏向特定的线程【认为下次还会来进行加锁 】
            System.out.println("释放偏向锁(101),带id:" + ClassLayout.parseInstance(user).toPrintable());
        }

        new Thread(() -> {
            synchronized (user) {
                System.out.println("轻量级锁(00):" + ClassLayout.parseInstance(user).toPrintable());
                try {
                    System.out.println("睡眠3s钟");
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("轻量-》重量(10): " + ClassLayout.parseInstance(user).toPrintable());
            }
        }).start();

        Thread.sleep(1000);

        new Thread(() -> {
            synchronized (user) {
                System.out.println("重量级锁(10):" + ClassLayout.parseInstance(user).toPrintable());
            }
        }).start();
    }
}

使用synchronized关键字的时候,对象有几种状态呢?

new一个对象、偏向锁、轻量、重量锁、gc标记

 

2. synchronized现在还重吗?

1.5之前,需要来回进行用户态和kernel来进行切换,所以是重量级锁

那么在1.5之后,对其进行了优化,引入了无锁、偏向锁、轻量级锁、重量级锁

同时:1.5之后,java增加了很多concurrentlock的内容,只用在java虚拟机内部就可以实现锁,也就是说不需要跟Operator System来打交到了,所以效率会提高

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值