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

相关文章

C#使用yield关键字实现提升迭代性能与效率

《C#使用yield关键字实现提升迭代性能与效率》yield关键字在C#中简化了数据迭代的方式,实现了按需生成数据,自动维护迭代状态,本文主要来聊聊如何使用yield关键字实现提升迭代性能与效率,感兴... 目录前言传统迭代和yield迭代方式对比yield延迟加载按需获取数据yield break显式示迭

c# checked和unchecked关键字的使用

《c#checked和unchecked关键字的使用》C#中的checked关键字用于启用整数运算的溢出检查,可以捕获并抛出System.OverflowException异常,而unchecked... 目录在 C# 中,checked 关键字用于启用整数运算的溢出检查。默认情况下,C# 的整数运算不会自

Springboot的ThreadPoolTaskScheduler线程池轻松搞定15分钟不操作自动取消订单

《Springboot的ThreadPoolTaskScheduler线程池轻松搞定15分钟不操作自动取消订单》:本文主要介绍Springboot的ThreadPoolTaskScheduler线... 目录ThreadPoolTaskScheduler线程池实现15分钟不操作自动取消订单概要1,创建订单后

C语言线程池的常见实现方式详解

《C语言线程池的常见实现方式详解》本文介绍了如何使用C语言实现一个基本的线程池,线程池的实现包括工作线程、任务队列、任务调度、线程池的初始化、任务添加、销毁等步骤,感兴趣的朋友跟随小编一起看看吧... 目录1. 线程池的基本结构2. 线程池的实现步骤3. 线程池的核心数据结构4. 线程池的详细实现4.1 初

Java子线程无法获取Attributes的解决方法(最新推荐)

《Java子线程无法获取Attributes的解决方法(最新推荐)》在Java多线程编程中,子线程无法直接获取主线程设置的Attributes是一个常见问题,本文探讨了这一问题的原因,并提供了两种解决... 目录一、问题原因二、解决方案1. 直接传递数据2. 使用ThreadLocal(适用于线程独立数据)

Oracle Start With关键字

Oracle Start With关键字 前言 旨在记录一些Oracle使用中遇到的各种各样的问题. 同时希望能帮到和我遇到同样问题的人. Start With (树查询) 问题描述: 在数据库中, 有一种比较常见得 设计模式, 层级结构 设计模式, 具体到 Oracle table中, 字段特点如下: ID, DSC, PID; 三个字段, 分别表示 当前标识的 ID(主键), DSC 当

线程的四种操作

所属专栏:Java学习        1. 线程的开启 start和run的区别: run:描述了线程要执行的任务,也可以称为线程的入口 start:调用系统函数,真正的在系统内核中创建线程(创建PCB,加入到链表中),此处的start会根据不同的系统,分别调用不同的api,创建好之后的线程,再单独去执行run(所以说,start的本质是调用系统api,系统的api

关键字synchronized、volatile的比较

关键字volatile是线程同步的轻量级实现,所以volatile性能肯定比synchronized要好,并且volatile只能修饰于变量,而synchronized可以修饰方法,以及代码块。随着JDK新版本的发布,synchronized关键字的执行效率上得到很大提升,在开发中使用synchronized关键字的比率还是比较大的。多线程访问volatile不会发生阻塞,而synchronize

java线程深度解析(六)——线程池技术

http://blog.csdn.net/Daybreak1209/article/details/51382604 一种最为简单的线程创建和回收的方法: [html]  view plain copy new Thread(new Runnable(){                @Override               public voi

java线程深度解析(五)——并发模型(生产者-消费者)

http://blog.csdn.net/Daybreak1209/article/details/51378055 三、生产者-消费者模式     在经典的多线程模式中,生产者-消费者为多线程间协作提供了良好的解决方案。基本原理是两类线程,即若干个生产者和若干个消费者,生产者负责提交用户请求任务(到内存缓冲区),消费者线程负责处理任务(从内存缓冲区中取任务进行处理),两类线程之