【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 的三种用法

  1. 锁普通方法
java 复制代码
public synchronized void method(){
}

锁的是:this对象

  1. 锁代码块
java 复制代码
synchronized(obj){
}

锁的是:obj对象

  1. 锁静态方法
java 复制代码
public static synchronized void method(){
}

锁的是:Class对象

ReentrantLock 是什么

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

相比synchronized更高级

使用方法:

java 复制代码
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 可以尝试获取锁

java 复制代码
lock.tryLock()

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

而synchronized

只能等待。

3 可以设置公平锁

java 复制代码
new ReentrantLock(true)

公平锁:

先来先得

synchronized:

不支持公平锁

4 可以中断等待线程

java 复制代码
lock.lockInterruptibly()

线程可以:被中断

synchronized 不支持。

真实应用场景

简单同步

用synchronized

比如:

计数器 简单资源保护

java 复制代码
public synchronized void add(){
    count++;
}

复杂锁控制

用ReentrantLock

比如:

需要尝试获取锁

需要超时

需要公平锁

java 复制代码
if(lock.tryLock()){
   // 执行
}

高并发组件

用ReentrantLock

比如:

线程池

并发容器

阻塞队列

总结表

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

syncronized锁升级的过程

先理解为什么需要锁升级

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

线程阻塞

线程唤醒

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

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

这就是锁升级的由来。

锁升级的整体过程

升级过程:

无锁

↓

偏向锁

↓

轻量级锁

↓

重量级锁

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

第一阶段无锁状态

第二阶段偏向锁

当第一个线程访问到

java 复制代码
synchronized(obj){
}

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

偏向锁的含义:

锁偏向某个线程

对象头会记录:

线程ID

之后如果线程A再进入:

不需要加锁

不需要CAS

直接执行

性能接近于:无锁

第三阶段:轻量级锁

此时如果另一个线程来了 :线程B

线程B尝试获取锁

此时JVM发现:锁已经偏向线程A

于是:偏向锁失效

升级为:轻量级锁

轻量级锁的核心思想是:

自旋

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

循环尝试获取锁

例如:

while(锁没释放){

再试一次

}

这样可以避免:

线程阻塞

线程唤醒

性能比重量级锁好很多。

第四阶段:重量级锁

如果线程越来越多:

线程A

线程B

线程C

线程D

此时自旋会消耗大量CPU。

JVM就会判断

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

特点:

线程会被阻塞

进入:

操作系统的Mutex锁

流程变为:

线程A执行

线程B阻塞

线程C阻塞

等线程A释放锁后:

操作系统唤醒其他线程

锁升级流程图

线程访问同步代码

│

▼

无锁

│

▼

偏向锁

(单线程访问)

│

▼

轻量级锁

(少量竞争)

│

▼

重量级锁

(激烈竞争)

存在的意义

总结:锁升级,就是为了

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

发明的产物

相关推荐
CV工程师丁Sir26 分钟前
ArkWeb 手记 04|DevTools 调试与缓存清理
java·spring·缓存·harmonyos
Sarvartha7 小时前
final 关键字
java·开发语言
2601_952047797 小时前
单一策略深度教程:用轻易云把集成任务的报错实时推送到钉钉机器人
java·机器人·钉钉
程序员Sunday7 小时前
JavaScript 事件循环面试题,宏任务与微任务怎么执行|Sunday面试指南
开发语言·javascript·面试·校招·事件循环·程序员sunday
数据狐(Datafox)8 小时前
淘宝图片搜索 API 落地实战:基于以图搜货搭建跨境电商选品系统
java·大数据·微服务
zhangzeyuaaa8 小时前
Ruby 多线程、GVL(GIL)与 Mutex 完全指南
开发语言·前端·ruby
刚子编程9 小时前
我用 ASP.NET Core 做了个水稻病虫害检查系统
后端·asp.net
谢亮_vipxieliang9 小时前
用 PHP 构建轻量级 REST API:从路由到鉴权的完整实践
开发语言·后端·php