java并发之并发关键字

并发关键字

关键字一:volatile

可以这样说,volatile 关键字是 Java 虚拟机提供的轻量级的同步机制

功能

volatile 有 2 个主要功能:

  • 可见性。一个线程对共享变量的修改,其他线程能够立即得知这个修改。普通变量不能做到这一点,普通变量的值在线程间传递需要通过主内存来完成。
  • 禁止指令重排序

底层原理

加入 volatile 关键字时,会多出 lock 前缀指令, 该 lock 前缀指令相当于内存屏障,内存屏障会提供 3 个功能:

  • 在执行到内存屏障这句指令时,在其前面的操作都已完成了
  • 强制将 处理器行的数据缓存写回内存
  • 一个处理器的缓存回写到内存会导致其他工作内存中的缓存失效

应用场景

  • 状态标记

    volatile + boolean

  • DCL 单例模式 (Double Check Lock,双重校验锁)

    public class Singleton {

        private volatile static Singleton singleton=null;

        private Singleton(){}

        public static Singleton getSingleton(){
            if(singleton==null){
                synchronized (Singleton.class){
                    if(singleton==null){
                        singleton=new Singleton();
                    }
                }
            }
            return singleton;
        }
    }

关键字二:synchronized

线程安全问题:

  • 存在共享数据(临界资源)
  • 存在多条线程共同操作这些共享数据

解决:同步机制

同一时刻有且只有一个线程在操作共享数据,其他线程必须等到该线程处理完数据后再对共享数据进行操作。

同步的前提:

  • 多个线程
  • 多个线程使用的是同一个锁对象

同步的弊端:

当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。

功能

使用 synchroinzed 进行同步,可以保证原子性(保证每个时刻只有一个线程执行同步代码)和可见性(对一个变量执行 unlock 操作之前,必须把变量值同步回主内存)。

使用

synchronized 修饰的对象有几种:

  • 修饰一个类。

    作用范围是 synchronized 后面括号括起来的部分

    作用对象是这个类的所有对象

    class ClassName {
       public void method() {
          synchronized(ClassName.class{
             // todo
          }
       }
    }
  • 修饰一个方法:被修饰的方法称为同步方法。

    作用范围是整个方法

    作用对象是调用这个方法的对象

    public synchronized void method(){
       // todo
    }
  • 修饰一个静态的方法。

    作用的范围是整个方法

    作用对象是这个类的所有对象

    public synchronized static void method() {
       // todo
    }
  • 修饰一个代码块:被修饰的代码块称为同步语句块

    作用范围是大括号 {} 括起来的代码块

    作用对象是调用这个代码块的对象

注意:如果锁的是类对象的话,尽管new多个实例对象,但他们仍然是属于同一个类依然会被锁住,即线程之间保证同步关系

原理

public class SynchronizedDemo {
    public static void main(String[] args) {
        synchronized (SynchronizedDemo.class//锁住类对象
        }
        method();
    }

    private synchronized static void method() //锁住类对象
    }
}

任意一个对象都拥有自己的 Monitor,当这个对象由同步块或者同步方法调用时, 执行方法的线程必须先获取该对象的 Monitor 才能进入同步块和同步方法, 如果没有获取到 Monitor 的线程将会被阻塞在同步块和同步方法的入口处,进入到 BLOCKED 状态。

synchronized 同步语句块的实现使用的是 monitorenter 和 monitorexit 指令。

monitorenter 指令指向同步代码块的开始位置,monitorexit 指令则指明同步代码块的结束位置。

使用 synchronized 进行同步,其关键就是必须要对对象的 Monitor 进行获取, 当线程获取 Monitor 后才能继续往下执行,否则就只能等待。 而这个获取的过程是互斥的,即同一时刻只有一个线程能够获取到 Monitor

上面的 SynchronizedDemo 中在执行完同步代码块之后紧接着再会去执行一个静态同步方法,而这个方法锁的对象依然就这个类对象, 那么这个正在执行的线程还需要获取该锁吗?

答案是不必的,从上图中就可以看出来, 执行静态同步方法的时候就只有一条 monitorexit 指令,并没有monitorenter 获取锁的指令。 这就是锁的重入性, 即在同一线程中,线程不需要再次获取同一把锁。 synchronized 先天具有重入性。 每个对象拥有一个计数器,当线程获取该对象锁后,计数器就会 +1,释放锁后就会将计数器 -1。

synchronized 修饰的方法并没有 monitorenter 指令和 monitorexit 指令,取得代之的是 ACC_SYNCHRONIZED 标识,该标识指明了该方法是一个同步方法,JVM 通过该 ACC_SYNCHRONIZED 访问标志来辨别一个方法是否声明为同步方法,从而执行相应的同步调用。

锁优化策略

JDK 1.6 之后对 synchronized 进行优化。

锁的 4 种状态:无锁、偏向锁、轻量级锁、重量级锁。

1. 自旋锁

在很多应用上,共享数据的锁定状态只会持续很短一段时间。自旋锁的思想是让一个线程在请求一个共享数据的锁时执行忙循环(自旋)一段时间,如果在这段时间内能获得锁,就可以避免进入阻塞状态。

自旋锁虽然能避免进入阻塞状态从而减少开销,但是它需要进行忙循环操作占用 CPU 时间,它只适用于共享数据的锁定状态很短的场景。

在 JDK 1.6 中引入了自适应的自旋锁。自适应意味着自旋的次数不再固定了,而是由前一次在同一个锁上的自旋次数及锁的拥有者的状态来决定。

2. 锁消除

锁消除是指对于被检测出不可能存在竞争的共享数据的锁进行消除

锁消除主要是通过逃逸分析来支持,如果堆上的共享数据不可能逃逸出去被其它线程访问到,那么就可以把它们当成私有数据对待,也就可以将它们的锁进行消除。

对于一些看起来没有加锁的代码,其实隐式的加了很多锁。例如下面的字符串拼接代码就隐式加了锁:

public static String concatString(String s1, String s2, String s3) {
    return s1 + s2 + s3;
}Copy to clipboardErrorCopied

String 是一个不可变的类,编译器会对 String 的拼接自动优化。在 JDK 1.5 之前,会转化为 StringBuffer 对象的连续 append() 操作:

public static String concatString(String s1, String s2, String s3) {
    StringBuffer sb = new StringBuffer();
    sb.append(s1);
    sb.append(s2);
    sb.append(s3);
    return sb.toString();
}Copy to clipboardErrorCopied

每个 append() 方法中都有一个同步块。虚拟机观察变量 sb,很快就会发现它的动态作用域被限制在concatString() 方法内部。也就是说,sb 的所有引用永远不会逃逸到 concatString() 方法之外,其他线程无法访问到它,因此可以进行消除。

3. 锁粗化

如果一系列的连续操作都对同一个对象反复加锁和解锁,频繁的加锁操作就会导致性能损耗

上一节的示例代码中连续的 append() 方法就属于这类情况。如果虚拟机探测到由这样的一串零碎的操作都对同一个对象加锁,将会把加锁的范围扩展(粗化)到整个操作序列的外部。对于上一节的示例代码就是扩展到第一个 append() 操作之前直至最后一个 append() 操作之后,这样只需要加锁一次就可以了。

4. 偏向锁

在大多数情况下,锁不仅不存在多线程竞争,而且总是由同一个线程多次获得。偏向锁的思想是偏向于第一个获取锁对象的线程,这个线程在之后获取该锁就不再需要进行同步操作,甚至连 CAS 操作也不再需要。

5. 轻量级锁

轻量级锁是由偏向锁升级而来,偏向锁运行在一个线程进入同步块的情况下,当第二个线程加入锁争用的时候,偏向锁就会升级为轻量级锁。

对于绝大部分的锁,在整个同步周期内都是不存在竞争的,因此也就不需要都使用互斥量进行同步,可以先采用 CAS 操作进行同步,如果 CAS 失败了再改用互斥量进行同步。

volatile 与 synchronized 比较

  • volatile 是 JVM 轻量级的同步机制,所以性能比 synchronized 要好

  • volatile 只能修饰变量

    synchronized 可以修饰代码块或者方法

  • 多线程访问 volatile 不会出现阻塞

    synchronized 会出现阻塞

  • volatile 只能保证可见性,不能保证原子性

    synchroinzed 能保证原子性,也间接保证了可见性

  • volatile 修饰的变量不会被编译器优化

    synchronized 修饰的变量可以被编译器优化

获取更多干货内容,记得关注我哦。

本文由 mdnice 多平台发布

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1546773.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

将Docker镜像推送到阿里云仓库,使用Docker-compose将mysql、redis、jar包整合在一起

进入阿里云: https://cr.console.aliyun.com 阿里云镜像控制台 选择个人实例 创建命名空间 创建镜像仓库 下一步之后,创建我们的本地仓库 创建好之后可以在个人实例里看到我们刚创建好的镜像仓库 点击我们的仓库进去里面,可以看到里面有我们…

4.5 了解大数据处理基本流程

文章目录 1. 引言2. 数据采集2.1 数据库采集2.2 实时数据采集2.3 网络爬虫采集 3. 数据预处理3.1 数据清洗3.2 数据集成3.3 数据归约3.4 数据转换 4. 数据处理与分析4.1 数据处理4.2 数据分析 5. 数据可视化与应用5.1 数据可视化5.2 ECharts框架5.3 课堂作业 6. 结语 1. 引言 …

信息安全工程师(17)密码体制分类

前言 密码体制,也称为密码系统,是信息安全领域中的一个重要概念,用于解决机密性、数据完整性、认证、身份识别、可控性及不可抵赖性等问题。根据加密和解密所使用的密钥是否相同,密码体制主要可以分为两大类:单钥体制&…

【Unity基础】Unity中跨平台使用SQLite(一)

一、概述 游戏中数据存储方案众多,在进行选择时,除了要考虑数据类型外,还要考虑对于跨平台的支持。 对于数据存储方案将在单独文章里介绍,下面只是总结了不同方案的特点。 方法跨平台支持读写能力适合场景特点Resources优秀只读…

c++ 使用 Graham 扫描的凸包(Convex Hull using Graham Scan)

先决条件: 如何检查两个给定的线段是否相交? c https://blog.csdn.net/hefeng_aspnet/article/details/141713655 java https://blog.csdn.net/hefeng_aspnet/article/details/141713762 python https://blog.csdn.net/hefeng_aspnet/article/details/…

PG duckdb插件 pg_quack部署与使用

一.pg_quack简介 pg_quack 是一个创新的 PostgreSQL扩展,它将 DuckDB-—一个嵌入式列式数据库 管理系统集成到PostgreSQL中。这个开源项目为开发者提供了一种在同一个数据 库环境中利用高性能数据处理和存储的新方式,使得在PostgreSQL在OLAP的性能 上得到了很大的提…

Spring Boot 进阶-第一个程序HelloWorld

从我们学习编程语言开始,每次入门一个语言都是从Hello World开始,当然这里我们也不例外。首先从一个简单的HelloWorld程序开始。 既然是要学着做Java Web开发,那么首先需要了解的就是如何去编写一个RESTFul风格的接口,这里我们就需要引入一个pom的依赖。 <dependency&g…

Django设计批量导入Excel数据接口(包含图片)

Django设计批量导入Excel数据接口(包含图片) 目录 Django设计批量导入Excel数据接口(包含图片)示例xlsx文件接口详情前端上传FormData后端APIView调用函数 Django 4.2.7 openpyxl 3.1.5示例xlsx文件 接口详情 前端上传FormData …

自动驾驶规划算法(一):A*算法原理和代码(c++与python)

1. A*算法简介 A*算法&#xff08;A-star algorithm&#xff09;诞生于1968年&#xff0c;由彼得哈特&#xff08;Peter Hart&#xff09;、尼尔森尼尔森&#xff08;Nils Nilsson&#xff09;和伯特拉波特&#xff08;Bertram Raphael&#xff09;三位计算机科学家提出。它的…

基于大数据可视化的图书推荐及数据分析系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码 精品专栏&#xff1a;Java精选实战项目…

快速订餐:Spring Boot 点餐系统

第二章关键技术的研究 2.1相关技术 网上点餐系统是在Java MySQL开发环境的基础上开发的。Java是一种服务器端脚本语言&#xff0c;易于学习&#xff0c;实用且面向用户。全球超过35&#xff05;的Java驱动的互联网站点使用Java。MySQL是一个数据库管理系统&#xff0c;因为它的…

WanFangAi论文写作研究生论文写作神器在线生成真实数据,标注参考文献位置,表格公式代码流程图查重20以内,研究生论文开题报告写作技巧

撰写开题报告时&#xff0c;遭循以下结构将有助于你条理清晰地展示你的研究计划: 研究目标 1.研究背景:简要介绍你选择该研究题目的背景&#xff0c;阐述研究的重要性。 2.研究问题:明确阐述你的研究将解决的核心问题。 研究价值 1.理论价值:探讨你的研究在学术领域内的贡献&a…

C语言 | Leetcode C语言题解之第437题路径总和III

题目&#xff1a; 题解&#xff1a; /*** Definition for a binary tree node.* struct TreeNode {* int val;* struct TreeNode *left;* struct TreeNode *right;* };*/ //递归遍历树节点&#xff0c;判断是否为有效路径 int dfs(struct TreeNode * root, int ta…

基于RealSense D435相机实现手部姿态重定向

基于Intel RealSense D435 相机和 MediaPipe的手部姿态检测&#xff0c;进一步简单实现手部姿态与机器人末端的重定向。 假设已经按照【基于 RealSenseD435i相机实现手部姿态检测】配置好所需的库和环境&#xff0c;并且有一个可以控制的机器人接口。 一、手部姿态重定向介绍 …

【WRF运行第二期(Ubuntu)】ARWpost安装及错误总结

WRF运行第二期&#xff1a;ARWpost安装及错误总结 1 ARWpost介绍2 ARWpost安装2.1 ARWpost_V3安装前准备2.2 安装ARWpost2.3 修改Makefile文件2.4 修改configure.arwp文件2.5 生成可执行文件EXE另&#xff1a;报错1-Error: Type mismatch between actual argument at (1) and a…

【项目】基于Linux和C++的动态在线视频点播系统设计

文章目录 1. 前言1.1 源码1.2 项目简介1.3 实现内容1.4 涉及技术 / 环境 2. 整体架构2.1 服务器功能2.2 服务器结构 3. 前提步骤3.1 思路分析3.2 创建视频表 4. 后端 基本功能实现&#xff08;视频点播&#xff09;4.1 服务端工具类实现4.2 日志输出类4.3 数据库/表 管理类4.4 …

前端开发之代理模式

介绍 代理模式是一种结构型设计模式&#xff0c;它通过为一个对象提供一个代理对象来控制对该对象的访问。代理对象可以在访问真实对象之前或之后添加一些额外的操作。 class RealImg {fileName: string;constructor(fileName: string) {this.fileName fileName;}disPlay()…

ValueError: Out of range float values are not JSON compliant

可能原因一 可能原因二 数据里面有NaN

优化java中 HashMap 的容量](capacity值)

我们很多人都知道&#xff0c;分配比我们所需更多的内存可能会对应用程序的性能产生负面影响。因此&#xff0c;使用带有容量的构造函数创建列表可能会产生很大的不同。 但是&#xff0c;使用Maps时&#xff0c;这个优化步骤可能不是那么简单。在本文中&#xff0c;我们将学习…

Django 基础之启动命令和启动配置修改

Django启动 django启动一般可以通过ide或者命令启动 ide启动&#xff1a; 启动命令&#xff1a; python manage.py runserver该命令后续可以增加参数&#xff0c;如&#xff1a; python manage.py runserver 8081 python manage.py runserver 127.0.0.1:8082 注意&#xff1…