JAVA并发编程学习笔记06-synchronized及锁优化

Monitor

Monitor是属于操作系统层面的一个对象,由JAVA对象头指向,直接翻译过来是管程或监视器。主要分为如下三个部分。
在这里插入图片描述

锁竞争流程

  1. 当第一个线程尝试获取锁,检测到Owner为null,则直接获取,此时Owner指向该线程;在这里插入图片描述

  2. 当第二个、第三个线程尝试获取锁,发现Owner有指向,则将这两个线程存入EntryList;在这里插入图片描述

  3. 此时线程一执行到wait()方法,故放弃锁,将Owner置为null,然后将第一个线程存入WaitSet中;在这里插入图片描述

  4. 系统发现Owner为null,开始从EntryList中挑选一个线程开始执行,也就是所谓的锁竞争,竞争后,线程二赢得了锁的所有权,Owner重新指向县城二,线程三继续呆在EntryList中;在这里插入图片描述

  5. 线程二执行到notifyAll()方法,唤醒所有等待的线程,将所有处于WaitSet中的线程转移到EntryList中,但此时由于线程二并未执行完,依旧会继续执行,Owner依旧指向线程二;在这里插入图片描述

  6. 当线程二执行完,锁被释放,Owner再次被置为null,操作系统再次开始锁竞争,然后由线程一(或线程三)竞争到锁。在这里插入图片描述

synchronized关键字

synchronized是JAVA自带的一种隐式锁,进入synchronized代码块则加锁,出代码块则解锁,加锁解锁过程由JDK底层实现。锁对应的每个对象就对应一个Monitor对象,这也是为什么说要使用相同的对象加锁。

修饰静态方法:

锁的是类对象,即该类的所有对象的被synchronized和static修饰的方法公用一把锁。

@Slf4j
public class Test03 {
    public static void main(String[] args) {
        new Thread(LockObject::print1).start();
        new Thread(LockObject::print2).start();
    }
}

@Slf4j
class LockObject {

    public static synchronized void print1() {
        log.info("start print1");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("end print1");
    }

    public static synchronized void print2() {
        log.info("start print2");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("end print2");
    }
}

在这里插入图片描述
打印结果可以看到,虽然是不同对象的不同方法,但两个线程是串行的。

修饰普通方法:

锁的是对象本身,与上面的静态方法不一样,不同对象的方法互不干涉,只有同一对象的普通方法被synchronized修饰才会互斥(代码类似静态方法,不做演示)。

总结:

synchronized关键字本质是锁关键字指定的对象,只要指定对象一直就互斥,而修饰普通方法和静态方法,代码编写上并未指定锁对象,是JDK底层默认指定了对象实例和类对象。

锁优化

由于synchronized是由操作系统提供,故开销较大,从JAVA6开始,对synchronized关键字获取锁的方式进行了改进。

偏向锁

锁指向的对象,默认是可偏向的,当第一次加锁,转变为偏向锁,偏向锁开销是最小的,表示系统默认以后都只有该线程使用这把锁。

轻量锁

当锁对象偏向于线程1后,在锁释放后,线程2获取了锁,则由偏向锁升级为轻量锁。轻量锁的开销大于偏向锁,小于重量锁,表示线程之间会对同一把锁有竞争,但时间是错开的。

锁膨胀

当一个线程持有锁对象的过程中,另一个线程尝试获取锁时,会将锁升级为重量锁,这个过程称为锁膨胀。

自旋优化

当进行重量级锁竞争时,当发现Owner被占有时,不会立即进入EntryList等待(进入等待意味着会进行上下文切换,开销较大),而是先进行自旋重试,当重试了几次后,若成功,则获取锁,反之再进入EntryList等待。

批量重偏向

当同一类的不同对象,同时偏向于线程1后,此时触发批量偏向线程2,当这个偏向次数达到阈值20次时,系统会任务之前的偏向不合理,故会直接将该对象的所有偏向线程由线程1改为线程2,这个过程称为批量重偏向。

批量撤销

类似批量重偏向,当同一类对象由偏向锁升级为轻量锁的次数达到阈值40次时,系统会认为该对象不适合用偏向锁,此时系统会将所有该对象的偏向锁升级为轻量锁。

锁消除

当某些代码的锁不可能有其他线程竞争时,JVM会将代码优化,即将无用的锁代码去掉,这个过程称为锁消除,本质是一种底层的代码优化策略。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值