zxpnet网站 zxpnet网站
首页
前端
后端服务器
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

zxpnet

一个爱学习的java开发攻城狮
首页
前端
后端服务器
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 大后端课程视频归档
  • 南航面试题
  • 并发编程

    • jmm和volatile
    • Synchronized&Lock&AQS详解
      • 概念
        • 临界资源
        • 同步互斥访问
        • 显示锁和隐式锁
        • CAS
      • 锁分类
      • Synchronized详解
        • 1、synchronized作用
        • 2、加锁方式
        • 3、synchronized底层原理
      • 对象内存结构
      • 锁的膨胀升级过程
    • Synchronized&Lock&AQS详解
    • Atomic&Unsafe魔法类详解
    • Collections&Queue体系分析
    • juc基础
  • 性能调优

  • java8语法

  • lombok

  • 日志

  • 工具类

  • spring

  • mybatis

  • springboot

  • redis

  • zookeeper

  • springcloud

  • dubbo

  • netty

  • springsecurity

  • mq消息中间件

  • shiro

  • beetle

  • 模板引擎

  • jpa

  • 数据结构与算法

  • 数据库知识与设计

  • gradle

  • maven

  • bus

  • 定时任务

  • docker

  • centos

  • 加解密

  • biz业务

  • pigx项目

  • 开源项目

  • 品达通用权限项目-黑马

  • 货币交易项目coin-尚学堂

  • php

  • backend
  • 并发编程
shollin
2021-08-18
目录

Synchronized&Lock&AQS详解

  • 概念
    • 临界资源
    • 同步互斥访问
    • 显示锁和隐式锁
    • CAS
  • 锁分类
  • Synchronized详解
    • 1、synchronized作用
    • 2、加锁方式
    • 3、synchronized底层原理
  • 对象内存结构
  • 锁的膨胀升级过程

# 概念

# 临界资源

多线程编程中,有可能会出现多个线程同时访问同一个共享、可变资源的情况,这个资源我们称之其为临界资源;这种资源可能是:对象、变量、文件等。 共享:资源可以由多个线程同时访问。

可变:资源可以在其生命周期内被修改 引出的问题: 由于线程执行的过程是不可控的,所以需要采用同步机制来协同对对象可变状态的访问,那么我们怎么解决线程并发安全问题? 实际上,所有的并发模式在解决线程安全问题时,采用的方案都是 序列化访问临界资源。

# 同步互斥访问

即在同一时刻,只能有一个线程访问临界资源,也称作同步互斥访问。Java 中,提供了两种方式来实现同步互斥访问:synchronized 和 Lock 同步器的本质就是加锁, 加锁目的:序列化访问临界资源,即同一时刻只能有一个线程访问临界资源(同步互斥访问),将并发请求转变成了串行化执行,会降低部分性能。 不过有一点需要区别的是:当多个线程执行一个方法时,该方法内部的局部变量并不是临界资源,因为这些局部变量是在每个线程的私有栈中,因此不具有共享性,不会导致线程安全问题

# 显示锁和隐式锁

所谓的显示和隐式就是在使用的时候,使用者要不要手动写代码去获取锁和释放锁的操作。Synchronized为Jvm内置锁,不需要手动加锁与解锁,Jvm会自动加锁跟解锁,即为隐式锁。ReentrantLock 读法/rɪˈentrənt/ ,实现juc里Lock,实现是基于AQS实现,需要手动加锁跟解锁ReentrantLock lock(),unlock(); 其为显式锁

参考:Java并发之显式锁和隐式锁的区别 - kaizi1992 - 博客园 (cnblogs.com) (opens new window)

# CAS

CAS全称 Compare And Swap(比较与交换),是一种无锁算法。在不使用锁(没有线程被阻塞)的情况下实现多线程之间的变量同步。java.util.concurrent包中的原子类就是通过CAS来实现了乐观锁。

CAS算法涉及到三个操作数:

  • 需要读写的内存值 V。
  • 进行比较的值 A。
  • 要写入的新值 B。

当且仅当 V 的值等于 A 时,CAS通过原子方式用新值B来更新V的值(“比较+更新”整体是一个原子操作),否则不会执行任何操作。一般情况下,“更新”是一个不断重试的操作。

java.util.concurrent包中的原子类AtomicXXX,就是通过CAS来实现了乐观锁

# 锁分类

image-20210818171250287

参考:java中的各种锁详细介绍 - JYRoy - 博客园 (cnblogs.com) (opens new window)

# Synchronized详解

参考文章 synchronized底层实现原理及锁优化_Medlen-CSDN博客_synchronized底层原理 (opens new window)

# 1、synchronized作用

原子性:synchronized保证语句块内操作是原子的 可见性:synchronized保证可见性(通过“在执行unlock之前,必须先把此变量同步回主内存”实现) 有序性:synchronized保证有序性(通过“一个变量在同一时刻只允许一条线程对其进行lock操作”)

# 2、加锁方式

①.同步实例方法,锁是当前实例对象 (不用static 修饰方法,spring容器对象的话,需要是单例才锁得住)

②.同步类方法,锁是当前类对象 (用static 修饰方法)

③.同步代码块,锁是括号里面的对象

public synchronized String syncMethod(){}  // 锁类实例对象
public static synchronized String syncMethod(){}  // 锁类对象

private static Object obj=new Object();
public String syncMethod(){
	synchronized(obj){   // obj为锁
		...
	}
} 
1
2
3
4
5
6
7
8
9

# 3、synchronized底层原理

synchronized是基于JVM内置锁实现,通过内部对象Monitor(监视器锁)实现,基于进入与退出Monitor对象实现方法与代码块同步,监视器锁的实现依赖底层操作系统的Mutex lock(互斥锁)实现,它是一个重量级锁性能较低。每个对象都有一个自己的Monitor(监视器锁)

image-20210820145720961

当然,JVM内置锁在1.5之后版本做了重大的优化,如锁粗化(Lock Coarsening)、锁消除(Lock Elimination)、轻量级锁(Lightweight Locking)、偏向锁(Biased Locking)、适应性自旋(Adaptive Spinning)等技术来减少锁操作的开销,,内置锁的并发性能已经基本与Lock持平。

synchronized关键字被编译成字节码后会被翻译成monitorenter 和monitorexit 两条指令分别在同步块逻辑代码的起始位置与结束位置,其实质为jmm的8大内存原子操作中的lock与unlock。

image-20210820103137121

每个同步对象都有一个自己的Monitor(监视器锁),加锁过程如下图所示:

image-20210820104546838

那么有个问题来了,我们知道synchronized加锁加在对象上,对象是如何记录锁状态的呢? 答案是锁状态是被记录在每个对象的对象头(Mark Word)中,下面我们一起认识一下对象的内存布局

/** 
 * synchronized原理:查看字节码 MONITORENTER MONITOREXIT
 * @author zxp
 * @date 2021-08-20 10:33
 */
public class SynchronizedTest {

    private Object object = new Object();

    public synchronized void method1(){
        System.out.printf("锁对象的某个实例");
    }

    public synchronized static void method2(){
        System.out.printf("锁类Class");
    }

    public void method3(){
        synchronized (object) {
            System.out.printf("锁代码块");
        }
    }
    public static void main(String[] args) {
        System.out.println(11);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

image-20210820104247644

# 对象内存结构

HotSpot虚拟机中,对象在内存中存储的布局可以分为三块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。

  • 对象头:比如 hash码,对象所属的年代,对象锁,锁状态标志,偏向锁(线程)ID,偏向时间,数组长度(数组对象)等
  • 实例数据:即创建对象时,对象中成员变量,方法等
  • 对齐填充:对象的大小必须是8字节的整数倍

image-20210820151249301

# 锁的膨胀升级过程

锁的状态总共有四种,无锁状态、偏向锁、轻量级锁和重量级锁。随着锁的竞争,锁可以从偏向锁升级到轻量级锁,再升级的重量级锁,但是锁的升级是单 向的,也就是说只能从低到高升级,不会出现锁的降级。下图为锁的升级全过程:

image-20210820151536486
#并发编程
jmm和volatile
Synchronized&Lock&AQS详解

← jmm和volatile Synchronized&Lock&AQS详解→

最近更新
01
国际象棋
09-15
02
成语
09-15
03
自然拼读
09-15
更多文章>
Theme by Vdoing | Copyright © 2019-2023 zxpnet | 粤ICP备14079330号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式