Java多线程基础
来源:互联网 发布:mysql 间隙锁 编辑:程序博客网 时间:2024/06/05 15:09
java内存模型
java内存模型用来屏蔽掉各种硬件和操作系统的内存访问差异,实现java程序在各种平台下都能达到一致的内存访问效果
线程可以通过主内存来进行通信
而内存间交互操作,有下面8种,虚拟机实现时会保证下面的操作都是原子的,不可再分的。
- lock:锁定主内存的变量。如果一个变量被lock,会清空工作内存中此变量的值,在执行引擎使用这个变量前,需要重新执行load或assign操作初始化变量的值。
- unlock:把主内存变量解锁
- read:读取主内存变量
- load:把read到的主内存变量放入到工作内存的变量副本
- use:把工作内存中一个变量的值传递给执行引擎
- assign:把一个从执行引擎接收到的值赋给工作内存的变量
- store:把工作内存中一个变量的值传送到主内存中
- write:把store操作从工作内存汇总得到的变量的值放入主内存的变量中
java内存模型(JMM)还规定了在执行上述8种基本操作时必须满足的规则,来确保执行的正确性。
原子操作
- 处理器如何实现原子操作
- 使用总线锁保证原子性
- 使用缓存锁保证原子性
- java中实现原子操作
- 使用循环CAS实现原子操作,java的CAS操作会使用现代处理器上提供的高效机器级别的原子指令。
CAS存在的问题- ABA问题
- 循环时间开销长
- 只能保证一个共享变量的原子操作
- 使用锁机制 (JVM实现锁机制也都使用了循环CAS)
- 使用循环CAS实现原子操作,java的CAS操作会使用现代处理器上提供的高效机器级别的原子指令。
程序执行的顺序
在执行程序时,为了提高性能,编译器和处理器常常会对指令做重排序,大致有三种
1)编译器优化的重排序
编译器重新安排语句的执行顺序
2)指令级并行重排序
现代处理器采用了指令级并行技术来将多条指令重叠执行。
3)内存系统的重排序
现代处理器使用写缓冲区临时保存向内存写入的数据,以提高效率,这样会找出指令执行效果上的重排序。
为了保证内存可见性,java编译器在生成指令序列的适当位置会掺入内存屏障指令来禁止特定类型的处理器重排序。JMM把内存屏障指令分为4类。LoadLoad Barriers StoreStoreBarriers LoadStore Barriers StoreLoad Barriers。
所以我们在程序中看到的执行顺序不一定是实际上的执行顺序,heppens-before原则(现行发生原则)与as-if-serial语义保证我们能够正确写出按序执行的程序,保证了满足的规则的指令能够影响到在它之后执行的指令。
数据依赖性
控制依赖与猜测执行
as-if-serial
- 不管怎么重排序,单线程程序的执行结果不能被改变
- 在单线程中对存在控制依赖的操作重排序,不会改变执行结果;但在多线程程序中,对存在控制依赖的操作重排序,可能会改变程序的执行结果。
heppens-before
来确定哪些操作可以影响到那些操作
- 程序顺序原则 :
- 管程锁定规则 :一个unlock操作先行发生于后面对同一个锁的lock操作。后面是指时间上的先后
- volatile变量规则 :对一个volatile变量的写先行发生于后面对这个变量的读操作。后面是指时间上的先后
- 线程启动规则
- 线程终止规则
- 线程中断规则
- 对象终结规则
- 传递性
时间的先后顺序与先行发生原则之间基本没有太大关系,happens-before仅仅要求前一个操作(执行的结果)对后一个可见。前一个操作按顺序排在第二个操作之前。
- 对于会改变程序结果的重排序,JMM要求编译器和处理器必须禁止;不会改变程序的结果的,会被允许。
volatile
- volatile的特性
- 保证此变量对所有线程可见(对声明了volatile的变量进行写操作,jvm就会向处理器发送一条lock前缀的指令)
- 禁止指令重排序(通过内存屏障)
- 每次使用volatile变量前,必须先从主内存刷新最新的值
- 每次修改后必须立刻同步会主内存中
- volatile变量与普通变量的比较
- 对于普通变量,线程A修改了一个普通变量的值,然后向主内存进行回写,另一条线程B在线程A回写完成后,再从主内存进行读取操作,新变量的值才会对线程B可见。
- 而写一个volatile的变量的时候 ,JMM会把该线程对应的本地内存中的共享变量值刷新到主内存;在读的时候,会把该线程对应的本地内存置为无效,线程接下来从主内存中读取共享变量。
- volatile内存语义的实现:插入内存屏障,禁止重排序。
synchronized
- synchronized的特性
- 让临界区互斥执行
- 让释放锁的线程向获取同一个锁的线程发送消息
- synchronized的内存语义
- 线程释放一个锁,JMM会把线程对应的本地内存中的共享变量刷新到主内存中,线程向接下来要获取这个锁的某个线程发出修改了共享变量的消息
- 线程获取一个锁,JMM会把该线程对应的本地内存置为无效,从而使得被监视器保护的临界区代码必须从主内存中读取共享变量;也意味着该线程接收了之前某个线程发出的消息。
- 线程释放一个锁,JMM会把线程对应的本地内存中的共享变量刷新到主内存中,线程向接下来要获取这个锁的某个线程发出修改了共享变量的消息
- 实现:volatile和CAS 。JVM基于进入和退出Monitor对象来实现方法同步和代码块同步。Synchronized用的锁存在Java对象头中。
- 锁的升级:偏向锁(对象头的Mark word存储着指向当前线程的偏向锁)–>轻量级锁(在执行同步块之前,JVM会先在当前线程的栈帧中创建用于存储锁记录的空间,将对象头中的Mark Word复制到锁记录中,然后尝试使用CAS将对象头中的Mark Word替换为指向记录的指针。如果成功,当前线程获得锁;如果失败,表示其他线程竞争锁,当前线程尝试自旋来获取锁)–>重量级锁
final
- Java基础/Java多线程
- Java基础-多线程基础篇
- java多线程基础
- Java多线程编程基础
- java多线程开发基础
- Java多线程基础
- Java -- 多线程技术基础
- 【java】多线程基础
- Java基础:多线程
- Java语言基础:多线程
- Java语言基础:多线程
- Java语言基础:多线程
- java多线程基础分析
- Java多线程编程基础
- java 多线程基础
- Java基础_多线程
- Java多线程基础
- java多线程基础
- 点宏织造厂ERP生产管理系统解决方案
- 【Ex.】随机产生两个数计算之和
- 文字飞舞和合成
- JZOJ 100043. 【NOIP2017提高A组模拟7.13】第K小数
- PHP之一维数组、二维数组排序
- Java多线程基础
- DrawingView android上的一个自定义涂鸦控件
- FastJson解析处理内部类问题
- tomcat中运行PHP项目
- Word通配符的使用
- Java分布式应用技术架构介绍
- Java基础知识点
- AS连接不上手机
- 谁说数据少就不能用深度学习?