跳转至

并发编程

Java并发编程中涉及的概念包括多线程、线程池、同步、锁、并发容器、内存模型等概念。

并发编程的基础

并发编程的三要素,也是并发问题的源头:

  • 原子性,一个或多个操作在CPU执行过程中不能被中断,要么都成功要么都失败。由于CPU分时复用的存在,可能存在一个线程中执行一部分指令,另一部分线程中继续执行另一部分指令,执行的结果不一致;
  • 有序性,程序执行的顺序按照代码的先后顺序执行,为了优化代码执行的性能,编译器和处理器会对指令进行重排序,重排序分为3类:
    1. 编译器重排序。编译器在不改变单线程程序语义的前提下,可以重新安排代码执行的顺序。
    2. 指令级并行的重排序。现代处理器采用了指令级并行处理技术,将多条指令并行执行,在不存在依赖的前提下,处理器可以改变指令执行的顺序。
    3. 内存系统的重排序。处理器使用缓存和读写缓冲区,使得加载和存储看上去像是在乱序执行。
  • 可见性,一个线程对共享变量的修改,另一个线程可以会立刻看到修改后的值。可见性是由CPU的缓存导致的。

JVM内存模型(JMM)

解决有序性和可见性的直接办法是禁用缓存和编译优化,Java内存模型(Java Memory Model)规定了如何按需提供禁用缓存和编译优化的方法,即3个关键字(volatile、synchronized、final)和8个happens-before规则。

1、单一线程原则(Single Thread Rule)
2、管程锁定规则(Monitor Lock Rule)
3、volatile变量规则(Volatile Variable Rule)
4、线程启动规则(Thread Start Rule)
5、线程加入规则(Thread Join Rule) 6、线程终端规则(Thread Interruption Rule) 7、对象终结规则(Finalizer Rule) 8、传递性(Transitivity)

为了解决原子性问题,Java中引入了锁的概念。锁分为以下几类:

  • 乐观锁、悲观锁
  • 独享锁、共享锁
  • 公平锁、非公平锁
  • 互斥锁、读写锁
  • 可重入锁、非可重入锁
  • 独享锁、共享锁
  • 无锁、偏向锁、轻量级锁、重量级锁

线程安全

当多个先程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象就是线程安全的。

实现线程安全的方法:

  • 互斥同步,synchronized 和 ReentrantLock
  • 非阻塞同步,CAS,AtomicInteger,ABA
  • 无同步,栈封闭,ThreadLocal,可重入代码(Reentrant Code)

参考 Java并发知识体系详解