synchronize关键字和线程可见性

2024-01-23 12:20

本文主要是介绍synchronize关键字和线程可见性,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

synchronize关键字

synchronize 锁定的对象分别为:方法锁、静态代码块锁、静态方法锁,而锁的范围跟锁对象的生命周期息息相关。而锁对象的其实是按照生命周期来判断,分别为对象锁和类锁

  • 类锁: 也就是class类,class类在jvm运行后便加载到了jvm的方法区中,一般情况下类锁的生命周期是跟着jvm的运行产生关系
  • 对象锁: 也就是对象的实例,根据对象的生命周期,如果对象的实例的内存地址

mark word

在hotsport虚拟机中,存储一个对象分为三部分:对象头、对象体、对齐填充。而java的每一个对象都是object的子类,在object对象的底层包含了很多本地的信息,其中包括mark word的信息。mark word记录了锁的相关信息并保存在了对象头中,mark word的大小根据jvm的位数进行动态调整,而mark word在对象头的存储数据图如下
在这里插入图片描述

锁的基本情况

在java1.6之前增加synchronize关键字一般都是直接通过调用系统底层的阻塞来完成的,并且把其他等待线程挂起操作。
在java1.6之后对synchronize进行了优化加入了偏向锁,轻量级锁

  • 偏向锁:偏向锁其实是一种无锁化的操作,适应于只有一个线程访问的时候,通过对象头中记录的threadID和epho来判断当前线程是获取线程的是哪个是否已经偏向,通过CAS(乐观锁)的方式来对threadID进行修改。如果存在多个线程咋进行锁升级->轻量级锁,该情况比较少,可以通过jvm参数关闭偏向锁
  • 轻量级锁:轻量级锁是偏向锁的升级,也是一种无锁化的操作,他通过自旋锁来操作,自旋也就是for循环通过自旋一定的次数或者自适应来判断是否升级锁,自适应是在jvm中判断上次获取锁的自旋的次数来动态决定下次获取锁的次数。如果通过自旋以后还是没有获取锁对象,则进行锁膨胀为->重量级锁,也就是java1.6之前的锁。
  • 重量级锁:重量级锁是通过monitor,每一个java对象都会有一个monitor,当一个线程获取锁以后,会调用monitorenter的指令,在线程完成后调用monitorexit的指令(释放锁),monitor依赖于底层系统的互斥锁来完成的

wait

在monitor中维护了两个队列,一个是synchronize队列,用户存放等待锁的想吃,还有一个是wait队列,当类调用了wait的时候回释放当前锁,然后把当前线程存放到monitor的wait队列中,只有调用了notify的时候才唤醒wait队列的数据,把数据迁移到synchronize队列中去竞争锁。

volatile和HappenBefore

在java层面一般使用volatile来解决可见性和有序性的问题。在java底层提供了四种指令内存屏障:storestore,loadload,storeload,loadstore,在使用volition关键字的时候回使用storeload来使修改的时候在其他线程可见。
happenBefore 是一种规则:

  • 在一个线程中的任何一个操作都应该happenbefore于该线程的任意后续操作之中
  • volatile变量规则,对于volatile关键字修饰的写的操作一定happenbefore于后续volatile变量的读的操作
  • 传递性规则:如果A happenbefore B, B happenbefore C,C happenbefore D,那么A happenbefore D,
  • start规则,在线程start以前的所有操作都happenbefore 与线程执行的所有操作
  • join规则,如果线程A 调用 B.join成功,那么线程b的所有操作都早于线程a从b调用成功之后
  • 监视器规则,对于任何一个获取synchronize锁的线程的所有操作都早于后一个获取synchronize锁的线程

这篇关于synchronize关键字和线程可见性的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/636391

相关文章

Java中实现线程的创建和启动的方法

《Java中实现线程的创建和启动的方法》在Java中,实现线程的创建和启动是两个不同但紧密相关的概念,理解为什么要启动线程(调用start()方法)而非直接调用run()方法,是掌握多线程编程的关键,... 目录1. 线程的生命周期2. start() vs run() 的本质区别3. 为什么必须通过 st

Linux实现线程同步的多种方式汇总

《Linux实现线程同步的多种方式汇总》本文详细介绍了Linux下线程同步的多种方法,包括互斥锁、自旋锁、信号量以及它们的使用示例,通过这些同步机制,可以解决线程安全问题,防止资源竞争导致的错误,示例... 目录什么是线程同步?一、互斥锁(单人洗手间规则)适用场景:特点:二、条件变量(咖啡厅取餐系统)工作流

Java中常见队列举例详解(非线程安全)

《Java中常见队列举例详解(非线程安全)》队列用于模拟队列这种数据结构,队列通常是指先进先出的容器,:本文主要介绍Java中常见队列(非线程安全)的相关资料,文中通过代码介绍的非常详细,需要的朋... 目录一.队列定义 二.常见接口 三.常见实现类3.1 ArrayDeque3.1.1 实现原理3.1.2

SpringBoot3中使用虚拟线程的完整步骤

《SpringBoot3中使用虚拟线程的完整步骤》在SpringBoot3中使用Java21+的虚拟线程(VirtualThreads)可以显著提升I/O密集型应用的并发能力,这篇文章为大家介绍了详细... 目录1. 环境准备2. 配置虚拟线程方式一:全局启用虚拟线程(Tomcat/Jetty)方式二:异步

如何解决Druid线程池Cause:java.sql.SQLRecoverableException:IO错误:Socket read timed out的问题

《如何解决Druid线程池Cause:java.sql.SQLRecoverableException:IO错误:Socketreadtimedout的问题》:本文主要介绍解决Druid线程... 目录异常信息触发场景找到版本发布更新的说明从版本更新信息可以看到该默认逻辑已经去除总结异常信息触发场景复

Java 关键字transient与注解@Transient的区别用途解析

《Java关键字transient与注解@Transient的区别用途解析》在Java中,transient是一个关键字,用于声明一个字段不会被序列化,这篇文章给大家介绍了Java关键字transi... 在Java中,transient 是一个关键字,用于声明一个字段不会被序列化。当一个对象被序列化时,被

JAVA保证HashMap线程安全的几种方式

《JAVA保证HashMap线程安全的几种方式》HashMap是线程不安全的,这意味着如果多个线程并发地访问和修改同一个HashMap实例,可能会导致数据不一致和其他线程安全问题,本文主要介绍了JAV... 目录1. 使用 Collections.synchronizedMap2. 使用 Concurren

C#中async await异步关键字用法和异步的底层原理全解析

《C#中asyncawait异步关键字用法和异步的底层原理全解析》:本文主要介绍C#中asyncawait异步关键字用法和异步的底层原理全解析,本文给大家介绍的非常详细,对大家的学习或工作具有一... 目录C#异步编程一、异步编程基础二、异步方法的工作原理三、代码示例四、编译后的底层实现五、总结C#异步编程

Java并发编程必备之Synchronized关键字深入解析

《Java并发编程必备之Synchronized关键字深入解析》本文我们深入探索了Java中的Synchronized关键字,包括其互斥性和可重入性的特性,文章详细介绍了Synchronized的三种... 目录一、前言二、Synchronized关键字2.1 Synchronized的特性1. 互斥2.

Spring Boot3虚拟线程的使用步骤详解

《SpringBoot3虚拟线程的使用步骤详解》虚拟线程是Java19中引入的一个新特性,旨在通过简化线程管理来提升应用程序的并发性能,:本文主要介绍SpringBoot3虚拟线程的使用步骤,... 目录问题根源分析解决方案验证验证实验实验1:未启用keep-alive实验2:启用keep-alive扩展建