【Java多线程】JUC之Java并发包Concurrent发展简述(各版本JDK中的并发技术)

本章先对Java个版本中的主要并发技术进行简述。

一.JDK1.4及之前

在JDK1.4及之前的版本,主要提供的并发技术有:

  1. synchronized关键字
  2. volatile关键字
  3. 不变模式

1.volatile关键字

被volatile修饰的变量能保证器顺序性和可见性

  • 顺序性:

    • 对一个volatile变量的写操作先行发生于后面对这个变量的读操作。“后面”指时间上的先后顺序
  • 可见性

    • 当写一个 volatile 变量时,JMM 会把该线程对应的工作内存中的共享变量刷新到主内存。
    • 当读一个 volatile 变量时,JMM 会把该线程对应的工作内存置为无效,线程接下来将从主内存中读取共享变量。

volatile相比于synchronized/Lock是非常轻量级,但是使用场景是有限制的:

  • 对变量的写入操作不依赖于其当前值,即仅仅是读取和单纯的写入,比如操作完成、中断或者状态之类的标志
  • 禁止对volatile变量操作指令的重排序

实现原理

  • volatile底层是通过cpu提供的内存屏障指令来实现的。硬件层的内存屏障分为2种:Load Barrier 和 Store Barrier即读屏障和写屏障

内存屏障有两个作用:

  • 阻止屏障两侧的指令重排序
  • 强制把写缓冲区/高速缓存中的脏数据等写回主内存,让缓存中相应的数据失效

2.synchronized关键字

synchronized用于修饰普通方法、修饰静态方法、修饰代码块

  • 简单来说,synchronized关键字以同步方法和同步代码块的方式,为方法和代码块上的对象加锁。使得同一时刻,在这个对象上的多个线程,只能由持有这个对象锁的单个线程进行代码的调用执行,保证代码的原子性、可见性和有序性

实现原理: 使用对象的监视器(Monitor,也有叫管程的)进行控制

  • 进入/加锁时执行字节码指令MonitorEnter
  • 退出/解锁时执行字节码指令MonitorExit
  • 当执行代码有异常退出方法/代码段时,会自动解锁

使用哪个对象的监视器:

  • 修饰对象方法时,使用当前对象的监视器
  • 修饰静态方法时,使用类类型(Class 的对象)监视器
  • 修饰代码块时,使用括号中的对象的监视器
  • 必须为 Object 类或其子类的对象

3.不变模式

所谓不变模式,就是指:在并发编程中,为确保数据的一致性和正确性,使用一种不可改变的对象。依靠其不可变的性质,来确保在没有同步的情况下依旧保持一致性和正确性。

Java中不变模式相关技术有:

  • final关键字
  • String类型

这里只讲发展历程, 更多内容,可以在我相关的文章中了解学习

二.JDK55

众所周知,JDK5是Java发展的一个重要版本,提供了很多技术,如泛型 Generic、枚举类型 Enumeration、可变参数varargs、注解 Annotations等等

  • 在JDK1.5版本中,也提供了对并发编程极为重要的一个包:java.util.concurrent(并发包)

java.util.concurrent(并发包)提供了一些列较为给力的并发技术,主要有:

  • 原子(Atomic)类型:如AtomicInteger、AtomicReference等,保证变量的原子性和可见性
  • 显式锁(Lock)接口:对之前版本锁机制的重构,相较于synchronized 关键字,能够提供更加灵活的特性,如:能够指定锁的意思就是每个线程都得执行到等待点进行等待,直到所有线程都执行到等待点,才会继续往下执行。相当于日常开会,只有等每个参会的人都到之后才会开始会议。、可以实现分组唤醒(Condition,类似于wait和notify)、是性能更好的锁。主要包括:Lock接口、ReadWriteLock接口和Condition接口
  • 计数器(CountDownLatch):利用它可以实现类似计数器的功能,类似于join()方法,使一个线程等待其他线程执行完毕后再执行。如有一个任务A,它要等待其他4个任务执行完毕之后才能执行。
  • 回环栅栏(CyclicBarrier):通过它可以实现让一组线程等待至某个状态之后再全部同时执行。叫做回环是因为当所有等待线程都被释放以后,CyclicBarrier可以被重用

    意思就是每个线程都得执行到等待点进行等待,直到所有线程都执行到等待点,才会继续往下执行。相当于日常开会,只有等每个参会的人都到之后才会开始会议。

  • 信号量(Semaphore):Semaphore可以控制同时访问特定资源的线程个数,通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可。
  • 并发集合:即集合类在并发环境下的版本。主要有:BlockingQueue(Queue)、ConcurrentMap(Map)、ConcurrentHashMap(HashMap)、CopyOnWriteArrayList(ArrayList)。
  • Callable和Future接口:为了解决继承Thread类和实现Runnable接口存在的弊端(不允许声明检查型异常,不能定义返回值),而引入的线程的新的定义方式。
  • 执行器(Executor接口):Executors相关类隐藏了如何处理Runnable的细节,提供了一组方法,能够创建拥有完善配置的线程池和executor。

这里只讲发展历程, 关于原子(Atomic)类型、显式锁(Lock)接口、并发集合、Callable和Future接口、执行器(Executor接口)的更多内容,可以在我相关的文章中了解学习

三.JDK7

JDK1.7版本中,主要提供的并发编程技术有:

  • TransferQueue:比BlockingQueue性能更好的并发集合实现。
  • 分支合并(Fork/Join)框架:运用分治法(divide-and-conquer)的思想,实现线程池中任务的自动调度,并且这种调度对用户来说是透明的,典型应用ForkJoinPool。

这里只讲发展历程, 关于分支合并(Fork/Join)框架)的更多内容,可以在我相关的文章中了解学习

四.JDK8

JDK.1.8版本中,主要提供的并发编程技术有:

  • 加法器(Adder)和累加器(Accumulator):原子类型的扩充与优化,主要有:LongAdder、LongAccumulator、DoubleAdder和DoubleAccumulator,比AtomicLong和AtomicDouble性能更优。
  • CompletableFuture:JDK5中Future的增强版。
  • StampedLockJDK5中ReadWriteLock的改进版。
上一篇:JAVA 内存模型(JMM)


下一篇:2021-10-11