java并发执行一个方法_JAVA的执行并发原理

Volatile

Volatile关键字用于确保共享数据的可见性与有序性,但是并不能保证方法的原子性,在程序中对Volatile关键字使用得当的话,它比synchronized的使用和执行成本会更低,因为他不会引起线程的上下文切换和调度。

先讲一下重排序,重排序是什么?

我们所编写的程序会经过编译器编译,然后写入内存中。在执行时,CPU会从内存中读取并执行,在这里,编译器与CPU为了提高程序执行时的效率,会对代码的执行顺序进行优化,但代码输出的结果并不会改变,所以从宏观上我们认为程序是按照我们的思路来运行的,这里有三种重排序:

1.编译器重排序:在不改变代码语义的情况下,对重新对代码执行顺序进行排序。

2.CPU重排序:我们的代码在CPU处理时,会被编译成各种指令,若不存在数据依赖性,处理器在执行代码语句时,可以对其生成的指令进行重排序。

3.缓存的重排序:在CPU对缓存进行读/写时,加载与存储的操作是存在乱序的。

所以在单线程运行情况下的,重排序是提升程序的执行效率,这些重排序对程序运行是无害的,而在多线程运行情况下,线程交替执行则会出问题。先看一个比较常见的例子:

class Counter{public static int count = 0;public static boolean flag = false;public void inc(){ count++; //-------操作1 flag = true; //-------操作2}public int getCount(){ if(falg){ flag = false; //-------操作3 return count; //-------操作4 } return 0;}

正常情况下是调用inc()后执行操作1与操作2,然后再调用getCount()执行操作3与操作4,但是再多线程情况下,如果对这段代码进行了重排序,很有可能会出现如下结果:

1.线程A调用了inc(),代码被重排序后执行顺序为先将flag置为ture,再对count进行自增。

2.而此时线程B调用了getCount(),此时线程A只运行了flag置为ture,还没有执行到对count自增,这时线程B就会返回非预期值。

在我们使用Volatile关键字时,JMM会向CPU指令中插入特定的指令来确保共享数据可见性与有序性。

1.内存屏障用于保障有序性

内存屏障是一组同步指令集,使得CPU与编译器在对加入内存屏障之前的所有读写操作都执行后才可以开始执行此点之后的操作。它用于保障程序的有序执行,被插入内存屏障指令的代码,会对其实际代码执行顺序进行限制,有以下四种内存屏障:

根据JSR-133 CookBook中的描述,我们可以从下表中得出结论:

1.如果第二个操作是Volatile写,那么无论第一个操作是什么类型的操作,都不能改变代码的执行顺序。它用于保障Volatile写之前的操作不会被重排序到其后执行。

2.如果第一个操作是Volatile读,那么无论第二个操作是什么类型的操作,都不能改变代码的执行顺序。它用于保障Volatile读之后的操作不会被冲排序到其前面执行。

为了达到上述规则,编译器在生成字节码时,会在指令中插入内存屏障来保证Volatile数据前后代码的执行顺序。

读:

1.在对一个Volatile读操作之后添加一个LoadLoad指令。(用来确保当前读操作之后的读操作都不会被重排序)

2.对于一个Volatile读操作之后添加一个LoadStore指令。(用来确保当前读操作于后续写操作之前进行)

写:

1.在对一个Volatile写操作之前添加一个StoreStore指令。(用来确保当前写操作之前的写操作不会被重排序到其后面执行)

2.在一个Volatile写操作之后添加一个StoreLoad指令。(用来确保当前写操作于之后读操作之前执行)

2.可见性

当共享变量被Volatile关键字修饰后,对Volatile变量进行读写都使JVM在变量前插入LOCK前缀指令,若不涉及缓存一致性协议,LOCK前缀指令会锁住总线,使其他CPU暂时无法通过总线访问内存,作用如下:

1.对被Volatile关键字修饰的变量进行写操作,Lock指令会直接将工作内存中的变量刷新至主存中。

1.对于被Volatile关键字修饰的变量进行读操作,Lock指令会令工作内存中的该变量失效,直接从主存中读取。

这里需要注意的是Volatile关键字只能修饰单个变量,它无法保障代码块的原子性,如a++这样的操作并不能保障它的原子性,因为它是由做个指令集组成。

volitatile的使用情景:

1.状态标记

2.double check

synchronized

synchronized关键字用于解决在并发编程时的有序性、原子性、可见性。相比于Volatile关键字,synchronized锁能够控制的范围更大,使用synchronized关键字修饰方法或代码块时,能够确保在同一时刻最多只有一个线程能够执行该代码。当synchronized修饰方法时,它锁住的是对象的实例synchronized(this),当作用在对象实例时,它锁住的是代码块。

cee6ac103e33a05011bd81d4f7aa67ca.gif

synchronized实现原理:

我们对于synchronized的理解也许只在于其互斥的特性,认为线程在执行加上synchronized关键字的代码,需要执行该代码块或方法的线程就会竞争该代码块或方法的互斥锁。若竞争成功则线程该代码块的执行权,而未竞争到该锁的线程只能被阻塞,等到持有锁的线程执行代码块或方法执行完毕后释放锁,其他线程才能继续竞争,而这仅仅synchronized关键字中重量锁的特性。其实synchronized在经过不断优化后,其锁的特性为偏向锁-》轻量锁-》重量锁三种,偏量锁和轻量锁在某种意义上能够减少重量锁带来的开销,但是他们都不能替代重量锁,下面就让我们来看看这三种锁的原理。

重量锁

重量锁故名思议就是需要消耗大量系统资源的锁,因为很重嘛。当多线程执行到具有synchronized关键字的代码时,会进行锁竞争,竞争失败的线程会进入一个阻塞队列,而获得锁的线程会获取代码的执行权。

而synchronized锁是一种非公平锁,当线程竞争失败时会阻塞,我们知道线程从运行状态切换到阻塞状态是依赖于操作系统从用户态切换到内核态来执行的,这种切换会消耗大量的系统资源(因为用户态与内核态都有各自专用的内存空间,专用的寄存器租等,用户态切换至内核态需要传递给许多变量、参数给内核,内核也需要保护好用户态在切换时的一些寄存器值、变量等,以便内核态调用结束后切换回用户态继续工作)。如果该方法是一个高频操作时,这将会消耗很多CPU处理时间。所以为了避免线程阻塞带来的消耗,引入了轻量锁。

轻量锁

轻量锁是为了避免在没有竞争的情况下重量锁所带来的开销,一旦该对象有多个线程竞争,轻量锁就会升级为重量锁,所以轻量锁和偏向锁并不能在多线程竞争情况下代替重量锁!!!只能在无锁竞争的条件下减缓重量锁的开销。在了解轻量锁前,我们先了解一下CAS操作与mark word标记,他们是实现轻量锁的基础。

CAS

CAS英文名(compare and swap)也就是比较交换,java语言在代码层面对其进行了封装,实际上是它是通过调用jni来实现的,它本质上是调用了cpu的指令集。在JUC中大量的使用到了CAS操作,它作为一种乐观锁,使用它就能实现所谓的无锁交换。

在CAS操作中,一个变量有三个状态值,一个是内存值V,一个是旧的预期值A,还有一个是要替换新值的B。对应到JMM中,V表示为主存中的值,而旧的预期值A为我们工作内存中的值,而B为操作后的新值,若V与B相等就说明该变量没有被其他线程修改,那么将变量替换为B值,不相等则不进行交换。所以使用CAS操作的开销相对于线程竞争过程中的阻塞唤醒引起的上下文切换来说小了很多(竞争情况下,操作队列,线程挂起,上下文切换)。

MARK WORD

我们知道java的Class文件是对java程序二进制文件格式的定义,java编译器将Class文件编译成字节码在jvm中运行,在堆内存中的对象都含有各自的对象头用于确定obj在运行时的状态,而Mark Word正是一个长为32bit的对象头,是用来标记同步线程的

这里先解释HashCode 与state两个变量的值,轻量锁与重量锁在HashCode中存入的值为指向占有锁的线程的栈中的存储该线程所占有锁的信息的地址(有点绕口,其实就是存了一个地址,下面会详细说),state表示当前对象所处的状态,下面我们来看一下轻量锁如何来实现锁机制,这里分两种情况。

该对象没有被其他线程锁定

因为轻量锁是由偏向锁升级而来,通过判断tag是否为1与锁标志位是否为01来得知对象是否有没有被其他线程占用,若没有被其他线程占用,jvm会在当前线程的栈中创建一个lock record空间,将当前需要被锁定的对象的mark word的拷贝副本存入到lock record中,然后尝试使用CAS操作将mark Word中的betifields字段中的值更新为指向lock record空间的地址,若CAS操作成功,则将state更新为00,表示轻量锁添加成功,当前线程拥有对该对象的执行权。

2.出现锁竞争或对象已经被其他线程占用

若有两个线程同时对未被锁定的对象上轻量锁时,会有一个线程竞争失败,此时竞争失败标志是CAS操作失败,则该线程会自旋一段时间,若还是CAS操作失败,则该轻量锁会升级为重量锁,竞争失败的线程进入阻塞状态,state标志置为10,且mark down中重量锁指针会被修改。当占有该轻量锁的线程释放锁时,竞争失败的锁会被唤醒,重新竞争锁。

2.unlock

解锁过程也是将lock record中存储的mark down副本与object头中mark down进行CAS操作,若两者相等,则说明没有其他线程竞争该轻量锁,释放成功。如果失败,则当前轻量锁存在竞争,则锁会升级为重量锁。

从上述轻量锁实现过程我们可以看到轻量锁是使用CAS操作来代替重量锁的互斥操作,在语言层面上实现了同步操作,这样能够节约许多系统开销,但是需要注意的是,这都是在无锁竞争的前提条件下,因为轻量锁并不能代替重量锁。

偏向锁

偏向锁是在JVM1.6中引入了,主要也是为了解决在没有竞争情况下锁性能的问题,通过上述轻量锁的讲解,我们了解到轻量锁是通过CAS操作来避免重量锁的阻塞开销。但是我们知道CAS操作也是需要通过本地调用来实现,归根到底还是通过CPU指令集的实现,JVM只是封装了该指令调用。所以CAS操作会产生一定的副作用。因为CPU通过总线来实现对内存中数据的读写,而多核CPU在将自身cache内存中的数据刷新至主存中时,会引触发“缓存一致性协议”,就是说CPU1对主存中的值进行了改变,“缓存一致性协议”会通知CPU2、CPU3自身cache中该值已经失效,需要重新读取。若在轻量锁中每次进入操作,若CAS操作很频繁的话,会给总线带来巨大的开销,而偏向锁就是为了避免这个开销产生的。

若线程在没有竞争的情况下去获取某一对象的锁,会通过CAS操作将自身的Thread ID 存入Mark Word中,如果CAS操作成功,则表示该线程拥有该对象的执行权,而偏向锁是具有可重入性的,偏向吗,就是偏袒第一次占有该对象锁的线程,当该线程再次竞争该对象的锁时,只需要对比较Mark Word中的Thread ID 与自身的Thread ID 是否相同,相同则表明没有其他线程竞争,可以继续使用;如果这时有线程来竞争,则该线程在执行完代码块后,偏向锁会升级为轻量锁。这里需要注意的是,偏向锁只有再有竞争时才会撤销,若没有竞争,则一直是第一次获得偏向锁的线程持有。我们可以看到偏向锁的出现更加降低了线程初次获取锁的开销。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java.util. 什么意思_java.util中,util是什么意义

展开全部1. util包的框架常用的集合类主要636f70793231313335323631343130323136353331333431343630实现两个“super接口”而来:Collection和Map。1.1 Collection有两个子接口:List和SetList特点是元素有序,且可重复。实现的常用集合类有Arra…

java测试用例编写_TestNG测试用例编写和执行

编写TestNG用例测试基本上包括以下步骤:编写业务逻辑针对业务逻辑中涉及的方法编写测试类,在代码中插入TestNG的注解直接执行测试类或者添加一个testng.xml文件运行 TestNG.下面我们介绍一个完整的例子来测试一个逻辑类;1.创建一个pojo类Empl…

好爽 java_Intellij是进行scala开发的一个非常好用的工具,可以非常轻松查看scala源码,当然用它来开发Java也是很爽的,之前一直在用scala ide和ec...

Intellij是进行scala开发的一个非常好用的工具,可以非常轻松查看scala源码,当然用它来开发Java也是很爽的,之前一直在用scala ide和eclipse,现在换成intellij简直好用到飞起,但是有些人不知道怎么用intellij去创建一个…

linux服务器安装php7_CentOS 7 下 PHP 7.1.12 安装配置

Linux系统:CentOS 7记录在CentOS 7 下 PHP 7.1.12 安装配置的过程。先安装相关依赖包yum installpcre pcre-devel zlib zlib-devel openssl openssl-devel gd gd-devel libjpeg libjpeg-devel libpng libpng-devel freetype freetype-devel e2fsprogs e2fsprogs-dev…

commvault备份mysql,备份MySQL数据库的4种方式

备份MySQL数据库的4种方式前言我们试着想一想, 在生产环境中什么最重要?如果我们服务器的硬件坏了可以维修或者换新, 软件问题可以修复或重新安装,但是如果数据没了呢?这可能是最恐怖的事情了吧, 我感觉在生产环境中应该没有什么比数据跟更为重要.那么我…

php $app-run(),Thinkphp 5.x 应用启动 App::run()

在上文加载完配置等一系列工作之后,进入App::run(),在run()方法中,首先通过自动加载机制拿到 Request 的一个实例接着 $config self::initCommon()初始化公共配置,先是 addNamespace 添加app当前所在的命名空间,然后 …

php react-native,React-Native+Mobx实现商城APP

这次给大家带来React-NativeMobx实现商城APP,React-NativeMobx实现商城APP的注意事项有哪些,下面就是实战案例,一起来看一下。最近一直在学习微信小程序,在学习过程中,看到了 wxapp-mall这个微信小程序的项目&#xff…

matlab axis 用法,MATLAB中regionprops的用法

Matlab图像处理函数:regionprops这里给出在Matlab图像处理工具箱中非常重要的一个图像分析函数:regionprops。顾名思义:它的用途是get the properties of region,即用来度量图像区域属性的函数。语法STATS regionprops(L,propert…

emlog_toolkit.php,emlog 4.0版本IIS6下伪静态划定规矩

emlog默许不能生成静态文件,不过彷佛有生成静态页面的相干插件,该插件博客吧先不研讨,本日博客簿要引见的是emlog 4.0版本在IIS6环境下的伪静态划定规矩,人人都晓得,经由过程伪静态能够让博客文章网址变得对搜索引擎越…

api.php phpcms,phpcms程序api怎么写接口

易站通,带你玩转PHPCMS建站程序,让你更快的熟悉该程序下面让我们来学习吧phpcms api怎么写接口?最近自己开发了一套crm系统,想着如果将来能卖出去,能不能再界面动态调用自己网站的推荐信息,算是一种广告吧&…

oracle 删除表 索引也会删除吗,Oracle 删除当前用户下所有的表、索引、序列

通过下面语句可以得到要删除Oracle的所有表、索引、序列... 的语句select drop table || table_name ||;||chr(13)||chr(10) from user_tables; --delete tablesselect drop view || view_name||;||chr(13)||chr(10) from user_views; --delete viewsselect drop sequence …

基于Java、Kafka、ElasticSearch的搜索框架的设计与实现

Jkes是一个基于Java、Kafka、ElasticSearch的搜索框架。Jkes提供了注解驱动的JPA风格的对象/文档映射,使用rest api用于文档搜索。项目主页:https://github.com/chaokunyang/jkes安装可以参考jkes-integration-test项目快速掌握jkes框架的使用方法。jkes…

Docker是传统的应用发布管理的终结者么?

译者注:使用Docker能真正改善传统的应用发布管理中遇到的问题么?以下是译文:自从2013年发布以来,Docker已经成为每一个操作管理者眼中的最爱。如果你一直与世隔绝,这里恰恰是你错过的部分。Docker是在一个操作环境地址…

基于Mesos/Docker构建数据处理平台

本文深入介绍了去哪儿网利用Mesos和Docker构建私有云服务的全过程,分享了从无状态应用向有状态应用逐步过度的经验与心得。平台概览2014年下半年左右,去哪儿完成了有关构建私有云服务的技术调研,并最终拍定了Docker/Mesos这一方案。下图1展示…

Mesos容器引擎的架构设计和实现解析

引言:提到容器,大家第一时间都会想到Docker,毕竟Docker是目前最为流行的容器开源项目,它实现了一个容器引擎(Docker engine),并且为容器的创建和管理、容器镜像的生成、分发和下载提供一套非常便…

阿里的盔甲、未来20年发展的动力以及对未来的洞察

刚刚变身迈克尔杰克逊,用“经济体”、“理想主义”等词刷屏的马云又在教师节那天,赶到2017世界物联网博览会,为阿里的物联网站台。过去18年以来,淘宝网、天猫、聚划算、全球速卖通、阿里巴巴国际交易市场、1688、阿里妈妈、蚂蚁金…

MySQL InnoDB Memcached Plugin在Oray公司的实践

1、应用背景介绍我所在职的Oray是一家提供各种互联网服务且具有海量用户的企业,我们也一直在实践各种新技术新架构;缓存方面,我们从memcached、ttserver、redis等都有较多应用,其中redis在我们的dns体系中有着很深度的集成使用&am…

网易数据运河系统NDC设计与应用

【导语】 NDC是网易近一年新诞生的结构化数据传输服务,它整合了网易过去在数据传输领域的各种工具和经验,将单机数据库、分布式数据库、OLAP系统以及下游应用通过数据链路串在一起。除了保障高效的数据传输外,NDC的设计遵循了单元化和平台化的…

想学区块链技术?来这!

2017年,区块链技术可谓是最热的宠儿。在国务院日前印发《“十三五”国家信息化规划》中,区块链技术和人工智能、虚拟现实、大数据、无人驾驶交通工具、基因编辑等新多项高新技术创新被定义为战略性前沿技术超前布局,在政府大方向认同的情况下…

oracle管理员登录报错,关于Oracle使用管理员账号登录失败的问题

我在本地建的Oracle数据库在调试自己写的存储过程的时候提示缺少 debug connect session 权限,一般情况下根据这个提示直接用管理员账号登录进去,执行grant debug connect session to 你的用户名这样的sql就行了,但是问题来了,当我…