本章先对Java个版本中的主要并发技术进行简述。
一.JDK1.4及之前
在JDK1.4及之前的版本,主要提供的并发技术有:
- synchronized关键字
- volatile关键字
- 不变模式
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的增强版。
-
StampedLock:
JDK5中ReadWriteLock的改进版。