Java并发热点问题

172 阅读3分钟

1、基本的并发包

  • java.util.concurrent 并发包
  • java.util.concurrent.atomic 原子性
  • java.util.concurrent.locks 锁相关

并发并行的区别:并发是多个线程公用一个资源、并行是做一件事不同的步骤同时执行。

2、通过Java关键字volatile引入 @VolatileDemo

volatile:Java虚拟机提供的轻量级的同步机制

  • 保证可见性
  • 保证原子性
  • 禁止指令重排

3、JMM(Java内存模型)

关键特点:

  • 可见性
  • 原子性
  • 有序性

有序性:

image-20201209231316702.png

volatile为什么能禁止指令重排?:内存屏障(Memory Barrier),一个cpu指令。

volatile通过插入读屏障和写屏障保证可见性,在volatile禁止重排序上,也是通过内存屏障实现的。 因为内存屏障可以使一些指令按照特定顺序执行。 volatile禁止指令重排序的规则:  1.当第二个操作是voaltile写时,无论第一个操作是什么,都不能进行重排序  2.当地一个操作是volatile读时,不管第二个操作是什么,都不能进行重排序  3.当第一个操作是volatile写时,第二个操作是volatile读时,不能进行重排序

这只是《Java虚拟机规范》中,对内存模型中主内存和线程工作内存的交互中规定的,是为了屏蔽不同的硬件和操作系统的数据访问细节。具体的规范是对操作JVM的字节码指令的规范中,比如主内存与线程工作内存的交互指令read、load、use、assign、store、write。不同平台和不同的厂商的不同版本的JVM有不同的实现,比如HotSpot、Jrocket、Sun Classic JVM。

image-20201209215638760.png

读取速率:硬盘 < 内存 < cpu

​ 由于速度的差异,cpu不能干等内存。故内存与cpu之间存在 缓存

image-20201209220333044.png

可见性:t1线程的工作内存修改了从主内存拷贝来的age,修改后赋值给主内存,若t2与t3等线程也可以更新为37,则为可见。

image-20201209224952962.png

原子性: 不可分割,要么同时成功,要么同时失败

​ 若不满足原子性,多线程n++操作的写覆盖导致数据丢失

4、CAS @CASDemo

什么是CAS:比较并交换。是一条CPU并发原语

线程在主内存中拷贝数据时会先拿到一个期望值,在线程工作内存中执行完后会检查当前值是否是期望值,若是则将执行后的值赋给主内存。

相较于synchronized,CAS不加锁不但保证了一致性同时也保证了较高的并发性。

image-20201209234504857.png

CAS底层原理:自旋锁 与 UnSafe

原子类 AtomicInteger 的 getAndIncrement 方法:

image-20201211232050922.png

image-20201211231802473.png

UnSafe类:

image-20201211233531212.png

  • 上图是一个线程中的自旋锁,var1(对象地址)的var2(该对象中某个属性的偏移量)对应一个值var5。while中不断比较:
    • var1与var2表示最新时刻的值,var5是刚刚获取的值,两者进行比较。若一致则跳出自旋锁,更新值。不一致则重新读取主内存的值赋给var5。
    • var5都是在主内存读取。
    • var5这个value被volatile修饰,所以具有线程间的可见性。

不仅仅只有整型的原子操作,自定义的原子操作通过原子引用实现: Class AtomicReference<> @ARDemo

CAS缺点

  • do while 自旋循环时间长,一直占用CPU开销很大
  • 只能保证 一个 共享变量的原子操作
  • 产生 ABA 问题

ABA问题: 线程2将主内存的值更改了,然后又改回来了!这时候线程1是不知道的,所以线程1以为没人动过。

那么如何进一步解决ABA问题?

  • 新增一种机制:时间戳AtomicStampedReference<>。相当于版本号 @ABADemo

5、Class CountDownLatch @CountDownLatchDemo

作用:控制 其他线程都完成了,最后一个线程再执行最后操作。到0 唤醒最后线程

6、Class CyclicBarrier @CyclicBarrierDemo

作用:控制 其他线程都完成了,最后一个线程再执行最后操作。到屏障 唤醒最后线程

7、Class Semaphore @SemaphoreDemo

信号量:主要用于两个目的:

  • 用于多个共享资源的互斥使用
  • 用于并发线程数的控制

8、创建线程的第三种方式 Callable @CallableDemo

9、线程池 @MyThreadPoolDemo、

image-20201217130535250.png

ThreadPoolExecutor 七大参数:

image-20201217132315993.png

10 synchronized 相关