jvm-5-高效并发

2 篇文章 0 订阅

五.高效并发

5.1 java内存模型与线程

java虚拟机规范中定义了一种内存模型,来屏蔽调各种硬件和操作系统的内存访问差异,以实现在各种操作平台内存访问的一致性。

5.1.1 java内存模型
  1. 这里有 “主内存”和“工作内存”的概念,和硬件中的主存、高速缓存类似,线程间变量值的传递均需要通过主存来完成,它和java内存模型的堆栈方法区基本没有关系,如果勉强对应一下,主内存->堆,工作内存->虚拟机栈。
  2. 内存间的交互,也就是变量在主存和工作内存的传递,java内存模型定义了8种操作来完成,每种操作都是原子的。(1)lock、unlock,(2)read,load,(3)use,assign,(4)store,write,这些都是成对出现的,第一对是对主存中变量的锁定和解锁,第二对是读主存写入工作内存,第三对是工作内存传值给执行引擎和执行引擎赋值给工作内存,第四对是工作内存的变量值传递给主存和主存的保存。
3.volatile 关键字

3.1 关键字的作用,(1).保证此变量对所有线程的可见性。(2).禁止指令重排序优化。第一点是通过每个工作线程在使用变量时都必须先刷新再使用,保证表变量的可见性。

3.2 适用场景:最适合使用的是一个线程写、其他线程读的场合,如果有多个线程并发写操作,仍然需要使用锁或者线程安全的容器或者原子变量来代替。原因:在并发场景下由于java运算并发原子操作导致依然是不安全的。

3.3 指令重排序优化,变量赋值的顺序与程序代码可能并不一致,指令重排序优化并非任意重排,只针对没有依赖关系的指令进行优化以提高速度,但是没有依赖关系的指令重排依然会引发问题(多核多线程并发),故此关键字可以禁止重排。在单线程中重排优化没有影响。

4.内存屏障

定义:为了防止编译器和硬件的不正确优化,使得对存储器的访问顺序(其实就是变量)和书写程序时的访问顺序不一致而提出的一种解决办法。

简单来说,一种是缓存不一致引起(两个cpu的缓存不一致),另一种是指令重排序优化引起(一个线程的指令先给变量赋值(有初始值),另一个线程的指令再使用,结果一优化,顺序反了)。

补充

通过volatile标记,可以解决编译器层面的可见性与重排序问题。而内存屏障则解决了硬件层面的可见性与重排序问题。

5.1.2 java与线程

线程定义:线程是CPU调度的基本单元。

实现线程主要有3中方式:(1)使用内核线程(KLT)实现,(2)使用用户线程(UT)实现,(3)使用用户线程加轻量级进程混合实现。

因为我们不直接使用内核线程,而是去使用内核线程的高级接口“轻量级进程”,这就是通常所说的线程,一个轻量级进程都有一个内核线程支持,故轻量级进程与内核线程的一对一关系称为“一对一线程模型”。

而一种进程与用户线程之间一对多的关系称为“一对多线程模型”,因为用户线程都是在用户态下执行的所以支持规模很大的线程数。现在各种语言基本都放弃了用户线程,因为为了解决一些问题问过于复杂

还有就是内核线程与用户线程配合使用的模式,其比例是不定的,故称为“多对多线程模型”。

补充

Sun JDK 使用的是一对一的线程模型。

线程调度

定义:为线程分配处理器的使用权,有两种方式:(1)协同式:由线程自己控制执行时间(2)抢占式:由系统分配执行之间,java采用的就是这个。注:协同式有利有弊,但是弊大于利。

线程状态

新建、运行、无限期等待、限期等待、阻塞、结束

没有箭头代表双向箭头

graph LR
A[new]-->|start|B[Running]
C[Waitting]---|wait|B
B---|synchronized|D[Blocking]
B---|sleep|E[Timed Waitting]
B-->|run结束|F[Terminated]

处于等待状态的线程收到notiyf()或notifyAll()时变为运行态。

5.1.3线程安全与锁优化
  1. 线程安全有强到弱可分为以下5类,不可变、绝对线程安全、相对线程安全、线程兼容、线程对立。

不可变即为被final修饰的,绝对线程安全代价极高、相对线程安全就是通常所说的线程安全、通常所说的一个类不是线程安全的是指“线程兼容”、无法在多线程环境中使用的代码既线程对立,由于java天生支持多线程故基本不存在。

5.1.3.1 线程安全的实现方法
  1. 互斥同步(阻塞同步),互斥是方法同步是结果,让两个线程互斥从而达到同步,有synchronized、ReentraLock(可重入锁),可重入锁有很多synchrinizd不具备的高级功能例如:等待可中断(等待的线程可中断去干其它事)、可实现公平锁(按队列顺序来获得锁)、锁绑定多个条件。
  2. 非阻塞同步,基于冲突检测的乐观并发策略,大概就是先使用,再判断共享数据是否有争用,没有就成功,有就再去其他措施,例如一直重试直到成功,因为不需要挂起线程故性能有所提高,这个需要硬件的支持,如现代才有的CASA指令,可以把语义上多个操作的行为只通过一个指令即可完成。
  3. 无同步方案,(1)可重入代码,特征:不依赖堆上的数据和公用的系统资源,用到的状态量都是参数传入,不调用非可重入方法,(2)线程本地存储,共享数据在同一个线程之内才有效,也就是线程独享,如ThreadLocal类中有一个ThreadLocalMap对象,存储本地线程变量。
补充

互斥同步属于悲观的并发策略,最主要的问题是线程阻塞唤醒带来的性能问题,而非阻塞同步正好解决了这个问题,性能提升,JDK5之后才可以使用CAS操作,而且不能直接使用,它由UnSafe类提供,不直接提供给用户,只能间接使用,如整数原子类AutomicInteger等就使用了Unsafe的CAD操作。

CAS:比较并替换,有三个操作数,内存地址V(主存中的变量值),旧的预期值A(线程缓存的值,也就是工作内存中的值),新值B(工作内存计算之后产生的新值),当工作内存新值产生后往主存中写入时,发生CAS,先比较工作内存中的旧值和主存的原值是否一致,一致则没有修改过,写入主存,不一致则重新计算,因为比较操作返回的是主存中的值,是“最新的”,线程拿这个值重新计算,CAS是***原子操作***。CAS 的ABA问题并不影响并发程序的正确性,解决的话,加入变量的版本号即可解决,J.U.C 提供了一个带有标记的原子***引用类***AtomicStampedReference,它和java中的4个引用类类似,强引用(默认),软引用,弱引用,许引用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值