博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
关于JAVA并发编程你需要知道的——语言篇
阅读量:4630 次
发布时间:2019-06-09

本文共 1680 字,大约阅读时间需要 5 分钟。

首先介绍一下重排序和内存屏障。

重排序分为两种

  • 编译器重排序:有一段代码 a=1.0;a=a/2.0;a=a+1;b=2; 由于现代CPU采用多级流水线设计,可以同时执行多条语句,因此上述代码执行过程中,b=2可以直接放入到流水线,而a=a+1必须等a=a/2.0完成以后才能放入流水线。为了提高代码执行效率,编译器会把代码改成a=1.0;a=a/2.0;b=2;a=a+1;。这就是编译器重排序,它确实改变了代码的执行顺序。
  • CPU指令重排序:虽然称为重排序,但是仅仅是因为指令执行需要的时钟周期不一样,加上流水线允许指令几乎同时执行,才使得执行结果的返回顺序不一样。但是指令放入流水线的顺序并没有改变

内存屏障是为了阻止以上两种重排序。在硬件篇中提到了mfence等就是为了阻止CPU指令重排序,通过对readBuffer和writebuffer的清空,让mfence之前的指令返回之后才能开始执行指令之后的程序。另外还有编译器级别的内存屏障 a=1.0;a=a/2.0;a=a+1;_asm__volatile_("":::"memory");b=2; ,这个禁止了编译器对代码的重排序,b=2的执行肯定在a=a+1之后

在Java语言标准中将内存屏障分为读读屏障、读写屏障、写写屏障、写读屏障。而在X86架构上的JVM具体实现,前三者其实是一样的,仅仅是编译器级别的内存屏障。只有写读屏障是CPU和编译器的内存屏障。之所以这样设计,是因为只要按照代码的逻辑来,读读、写写、读写这三种场景在X86架构上没有问题,但是写读这种场景必须保证读到的东西确实是刚才写的。

Java中并发原语其实只有两个

  1. Java在最开始的时候并发原语只有synchronized,这个关键词可以修饰在方法上,也可以修饰在代码块之前。修饰在静态方法上,那么竞争的是这个类,修饰在非静态方法上竞争的是这个对象,编译结果是给方法加上了ACC_SYNCHRONIZED标志。修饰在代码块上,就是在代码块之前加上monitorenter,代码块之后加上monitorexit。而在具体工作上两者其实没有区别,都是通过对类或者对象的头部进行CAS操作,夺取所有权。synchronized修饰的代码里面用到的变量第一次都是从缓存中读取,不会优化为寄存器变量
  2. 由于最开始的时候synchronized性能并不是很好,于是引入了另一个并发原语volatile,这个关键字应该是源于C语言。在C语言中volatile修饰的变量,不能被优化成寄存器变量,写的时候必须写到缓存,读的时候必须从缓存中读取,同时禁止编译器颠倒两个volatile变量的操作顺序。Java对volatile的语义进行了增强,禁止编译器对volatile变量和其他变量的操作顺序,也就是volatile的写操作之后和读操作之前都插入了写读屏障。volatile的使用场景和synchronized有所不同,volatile修饰的是变量,同一时刻只有一个线程对变量进行写入。synchronized修饰的是代码,同一时刻只有一个线程执行这段代码

从jdk1.6开始 jvm将synchronized锁分为偏向锁,轻量锁和重量锁。如果synchronized修饰的代码都是同一个线程在运行,那么synchronized底层实现就是偏向锁,如果jvm发现有多个线程执行这段代码,但是执行的时间不冲突,比如两个线程一个早上6点跑一次,另外一个晚上6点跑一次,两者没有发生任何的锁竞争,那么synchronized底层实现就是轻量锁,如果线程同时执行到了这段代码,发生了锁竞争,那么底层实现就是重量锁。

Java并发编程很大程度上就是做到线程之间的同步,JVM具体实现上也就是volatile和CMPXCHG指令。在此基础上产生了Atomic类、AQS和非阻塞数据结构,最后衍生出了Lock、阻塞队列、并发容器和同步器等并发数据结构。

转载于:https://www.cnblogs.com/hanzai/p/7050495.html

你可能感兴趣的文章
Struts2方法调用的三种方式
查看>>
Navicat工具多表查询
查看>>
第四章 读书笔记
查看>>
我不为人人,人人不为我
查看>>
iOS网络编程(三) 异步加载及缓存图片---->SDWebImage
查看>>
Qt qml 模拟iphone slide to unlock 的聚光动画文字效果
查看>>
查看线程的运行状态
查看>>
Flink学习笔记:Operators之CoGroup及Join操作
查看>>
[WCF] - Odata Service 访问失败,查看具体错误信息的方法
查看>>
【2019/4/30】周进度报告
查看>>
.net程序员面试题
查看>>
团队分数分配方法——BY 李栋
查看>>
docker获取镜像很慢解决办法
查看>>
学习-现代交换原理与通信技术
查看>>
【编程题目】左旋转字符串 ☆
查看>>
SQL Server 2008 R2如何开启数据库的远程连接
查看>>
笔记一:python安装和执行
查看>>
关于字符串的分割问题
查看>>
Tornado 类与类组合降低耦合
查看>>
2009 Competition Highlights by ICPC Live
查看>>