并发编程灵魂拷问系列之说说synchronized


并发编程灵魂拷问系列之synchronized

1.什么是synchronized,JDK1.6前后变化?

我们可以知道,并发问题一般都是因为 多个线程同时操作同一个共享数据 所造成的,解决办法就是加锁,例如 synchronized加锁

  • JDK1.6之前:synchronized使用的是操作系统的互斥锁(底层操作系统的mutex相关指令),因为需要在用户态和内核态进行切换,开销很大,导致并发激烈时性能很差
  • JDK1.6之后:进行了锁优化,比如锁升/分级、锁消除、锁粗化等,性能大幅提高,推荐使用

2.那synchronized底层原理是什么呢?

synchronized属于 非公平锁

当一个线程试图访问同步代码块时,它首先必须得到锁,退出或抛出异常时必须释放锁。

synchronized是基于Monitor来实现同步的,每一个对象都有对应着一个Monitor,如果需要加锁,则必须获取monitor的lock锁。

monitor锁是可重入锁,有一个计数器,当进入同步代码块时,执行monitorenter指令,计数器+1,退出一个同步代码块时,执行monitorexit指令,计数器-1;当发生对同一对象多次加锁时,计数器加减多次

  1. 对于同步方法,JVM采用 ACC_SYNCHRONIZED 标记符来实现同步。
  2. 对于同步代码块。JVM采用 monitorentermonitorexit 两个指令来实现同步。

3.什么是锁升/分级?

synchronized 一般来说都对 对象 进行加锁

这个可以从对象入手分析,对象在 的存储中存有对象头,对象头中的 Mark Word 存储着对象的运行时数据,包括对象的hashcode、对象持有的锁等,当中锁从状态进行了分类:

无锁、偏向锁、轻量级锁、重量级锁

线程获取锁,会根据锁竞争激烈程度进行锁升级来提高性能,锁可以升级但是不能降级

总结:

  1. 偏向锁:作者认为很多情况都是一个线程多次获取锁,这种情况加锁解锁最好不需要消耗太多资源,引入了偏向锁。当一个线程获取到锁,锁进进入了偏向模式(偏向锁),下一次该线程获取锁时,不需要额外进行额外进行操作,减少消耗。所以适合没有多线程来竞争锁的场景

  2. 轻量级锁:当其他线程来竞争原本偏向线程A的偏向锁时,等线程A释放锁后,该锁的偏向锁被撤销,升级为轻量级锁(只有发生了多线程之间的锁竞争,该锁会从偏向锁升级为轻量级锁)。
    轻量级锁生效后,当线程X持有锁时,线程Y获取该对象的锁被线程X持有,则进行 自旋 (循环使用CAS来获取锁)

  3. 重量级锁:当线程Y自旋超过一定时间或者次数,锁会升级为重量级锁,防止CPU空转(自旋过久)。升级为重量级锁后,线程Y不在主动获取锁,而是线程X释放锁之后通知线程Y去获取锁,线程Y从自旋变为阻塞。

简单来说:

  • 偏向锁:在Mark Word 记录线程ID进行比对
  • 轻量级锁:通过CAS + 自旋的方式竞争
  • 重量级锁:使用monitor对象,阻塞

4.什么是锁消除?

前文可知,线程安全问题条件之一是存在共享数据,但是当 synchronized 修饰的代码块中不存在共享数据时,JDK会通过逃逸分析判断这段代码不会有线程安全问题,自动消除这个锁

例子:

private static void someMethod() {
    Object object = new Object();
    synchronized(object) {
        System.out.println(object);
    }
}

someMethod方法中的object是局部变量,当多个线程进入方法,会new一个新的object,不存在共享数据,所以会进行 锁消除

5.什么是锁粗化?

将多个加锁解锁操作连接在一起,JDK会根据优化策略将其扩展成一个范围更大的锁

例子:

// 粗化前
public void doSomethingMethod1() {
    synchronized (lock) {
        // do some thing
    }
    // 能够很快执行完毕,且无需同步的代码
    synchronized (lock) {
        // do other thing
    }
}

// 粗化后:
public void doSomethingMethod2() {
    //进行锁粗化:整合成一次锁请求、同步、释放
    synchronized (lock) {
        // do some thing
        // 能够很快执行完毕,且无需同步的代码
        // do other thing
    }
}

6.synchronized都可以修饰哪些对象?

  • 1.修饰实例方法:锁定的是this对象(类锁)
  • 2.修饰静态方法:锁定的是class对象(对象内置锁)
  • 3.修饰代码块:锁定任意指定的对象

7.synchronized有什么缺点呢

  • 1.锁的粒度还是比较大的
  • 2.锁竞争激烈情况下,可能发生死锁
  • 3.锁竞争激烈时,因为串行化,多个线程排队竞争锁效率不会太高

【参考链接】:
1:synchronized和锁
2:Java中的锁原理、锁优化、CAS、AQS详解
3:由浅入深逐步了解 Synchronized
4:全网最细:17张图带你秒杀synchronized关键字
5:【对线面试官】synchronized


评论
  目录