【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释放锁后:

操作系统唤醒其他线程

锁升级流程图

线程访问同步代码

无锁

偏向锁

(单线程访问)

轻量级锁

(少量竞争)

重量级锁

(激烈竞争)

存在的意义

总结:锁升级,就是为了

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

发明的产物

相关推荐
wang09077 分钟前
自己动手写一个spring之MVC_3
java·spring·mvc
闲猫17 分钟前
Spring AI / Models / Chat Models / Ollama
java·人工智能·spring
汤米粥33 分钟前
Python爬虫中Xpath用法之——normalize-space()
开发语言·python
FREEDOM_X39 分钟前
Linux 多线程编程——总结
java·linux·运维·ubuntu
IguoChan40 分钟前
4. d2l — 模型选择、欠拟合和过拟合
后端
科技道人43 分钟前
记录 设置-显示大小和字体
开发语言·android设置·字体和显示大小
tmlx3I0811 小时前
Tomcat的架构设计和启动过程详解
java·tomcat
浮江雾1 小时前
Flutter第三节----Dart中的数据类型
android·开发语言·学习·flutter·入门·函数
weixin_422329311 小时前
AgentScope Java 项目入门 & Builder 深度解读
java·开发语言·人工智能
shepherd1261 小时前
一次把 Spring MVC 文件上传参数“查没了”的排查:multipart、Filter 与 request body 的连环坑
java·后端·spring·mvc