欢迎光临
我们一直在努力

【Java八股锁机制的认识】synchronized和reentrantlock区分,锁升级机制

synchronized和reentrantlock及其应用场景?

synchronized 是什么

synchronized 是 Java 提供的 内置锁机制。
核心一句话:

保证同一时间只有一个线程执行某段代码。

例如
同时调用三个线程:线程A,线程B,线程C
有synchronized的存在使得三个线程可以

A执行完
B再执行
C最后执行

避免了线程安全问题(A B C 同时执行)

synchronized 的本质

synchronized 是锁,更是对象监视器锁(Monitor Lock)

用synchronized锁住了obj类,当有线程进入到obj类中,就会获取到obj的锁。如果别人已经拿走了锁,就必须进行等待。

synchronized 的三种用法

  • 锁普通方法
  • public synchronized void method(){
    }

    锁的是:this对象

  • 锁代码块
  • synchronized(obj){
    }

    锁的是:obj对象
    3. 锁静态方法

    public static synchronized void method(){
    }

    锁的是:Class对象

    ReentrantLock 是什么

    ReentrantLock 是 Java并发包提供的一种锁。

    相比synchronized更高级

    使用方法:

    Lock lock = new ReentrantLock();

    lock.lock();
    try{
    // 临界区
    }finally{
    lock.unlock();
    }

    流程:

    加锁
    执行
    释放锁

    为什么叫 Reentrant(可重入)

    可重入表示:

    同一个线程可以多次获得同一把锁。

    举个例子:

    methodA()
    ↓
    methodB()

    两个方法都加锁:lock
    同一个线程进入 methodA 后:还能进入 methodB

    不会死锁。

    注意:

    synchronized 也是可重入锁

    所以:

    两者都是可重入锁

    ReentrantLock 比 synchronized好在哪?

    1 可以手动控制锁
    synchronized

    自动加锁 自动释放

    ReentrantLock

    手动 lock() 手动 unlock()

    2 可以尝试获取锁

    lock.tryLock()

    说明:如果拿不到锁,就直接返回。
    而synchronized

    只能等待。

    3 可以设置公平锁

    new ReentrantLock(true)

    公平锁:

    先来先得

    synchronized:

    不支持公平锁

    4 可以中断等待线程

    lock.lockInterruptibly()

    线程可以:被中断
    synchronized 不支持。

    真实应用场景

    简单同步

    用synchronized

    比如:

    计数器 简单资源保护

    public synchronized void add(){
    count++;
    }

    复杂锁控制

    用ReentrantLock

    比如:

    需要尝试获取锁
    需要超时
    需要公平锁

    if(lock.tryLock()){
    // 执行
    }

    高并发组件

    用ReentrantLock

    比如:

    线程池
    并发容器
    阻塞队列

    总结表

    特性synchronizedReentrantLock
    类型 JVM关键字 Java类
    加锁方式 自动 手动
    释放锁 自动 必须unlock
    可重入 支持 支持
    公平锁 不支持 支持
    tryLock 不支持 支持
    中断锁 不支持 支持
    复杂控制 弱 强

    syncronized锁升级的过程

    先理解为什么需要锁升级
    因为在早期JDK版本,syncronized性能很差,因为他使用的是操作系统级别的互斥锁(Mutex),一旦线程竞争锁,就会发生

    线程阻塞
    线程唤醒

    所以在JDK1.6版本之后,出现了优化:

    锁会根据竞争情况逐渐升级。

    这就是锁升级的由来。

    锁升级的整体过程

    升级过程:

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

    一句话理解:竞争越激烈,锁越重。

    第一阶段无锁状态

    第二阶段偏向锁
    当第一个线程访问到

    synchronized(obj){
    }

    JVM会认为只有一个线程会访问,于是进入偏向锁
    偏向锁的含义:

    锁偏向某个线程

    对象头会记录:

    线程ID

    之后如果线程A再进入:

    不需要加锁
    不需要CAS
    直接执行

    性能接近于:无锁

    第三阶段:轻量级锁
    此时如果另一个线程来了:线程B
    线程B尝试获取锁
    此时JVM发现:锁已经偏向线程A
    于是:偏向锁失效
    升级为:轻量级锁
    轻量级锁的核心思想是:

    自旋

    线程不会立即阻塞,而是:

    循环尝试获取锁

    例如:

    while(锁没释放){
    再试一次
    }

    这样可以避免:

    线程阻塞
    线程唤醒

    性能比重量级锁好很多。

    第四阶段:重量级锁
    如果线程越来越多:

    线程A
    线程B
    线程C
    线程D

    此时自旋会消耗大量CPU。

    JVM就会判断

    竞争过于激烈,将锁升级成重量级锁

    特点:

    线程会被阻塞

    进入:

    操作系统的Mutex锁

    流程变为:

    线程A执行
    线程B阻塞
    线程C阻塞

    等线程A释放锁后:

    操作系统唤醒其他线程

    锁升级流程图

    线程访问同步代码
    │
    ▼
    无锁
    │
    ▼
    偏向锁
    (单线程访问)
    │
    ▼
    轻量级锁
    (少量竞争)
    │
    ▼
    重量级锁
    (激烈竞争)

    存在的意义

    总结:锁升级,就是为了

    减少线程阻塞带来的性能开销。

    发明的产物

    赞(0)
    未经允许不得转载:171主机测评 » 【Java八股锁机制的认识】synchronized和reentrantlock区分,锁升级机制
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址