第五部分 并发

15 并发基础知识

15.1 线程的基本概念

15.1.1 创建线程

线程表示一条单独的执行流,它有自己的程序执行计数器,有自己的栈。

15.2 理解synchronized

15.2.2 进一步理解synchronized

  • 可重入性
  • 内存可见性
  • 死锁
  1. 可重入性

可重入,也就是,对同一个执行线程,它在获得了锁之后,在调用其他需要同样锁的代码时,可以直接调用。
可重入是通过记录锁的持有线程和数量来实现的。当调用被synchronized包含的代码是,检查对象是否已被锁,如果是,在检查是否被当前线程锁定,如果是,增加持有数量,如果不是被当前线程锁定,才加入等待队列,当释放锁时,减少持有数量,当数量变成0时才释放整个锁。

  1. 可见性

保证内存可见性:在释放锁是,所有写入都会写会内存,而获得锁后,都会从内存读取最新数据。
为了保证内存可见性,使用synchronized的成本有点高,有一个更轻量级的方法,那就是给变量加修饰符volatile。
加了volatile之后,Java会在操作变量是插入特殊的指令,保证读写到内存最新值,而非缓存的值。

  1. 死锁

使用synchronized或者其他锁,要注意死锁。

并发容器

  • CopyOnWriteArrayList
  • ConcurrentHashMap
  • ConcurrentLinkedQueue
  • ConcurrentSkipListSet

15.3 线程的基本协作机制

多线程协作的基本机制wait/notify

15.3.1 协作的场景

多线程之间协作的场景有很多,例如:

  1. 生产者/消费者协作模式;
  2. 同时开始:类似于运动员比赛,在听到比赛开始枪响后同时开始,在一些程序,尤其是模拟仿真程序中,要求多个线程能同时开始。
  3. 等待结束:主从协作模式也是一种常见的协作模式,主线程将任务分写为若干子任务,为每个子任务创建一个线程,主线程在继续执行其他任务之前需要等待子任务执行完毕。
  4. 异步结果:
  5. 集合点:类似于学校或公司组团旅游,在旅游过程中有若干集合点,比如触发集合点,每个人从不同地方来到集合点,所有人到齐后进行下一项活动,在一些程序,比如并行迭代计算中,每个线程负责一部分计算,然后在集合点等待其他线程完成,所有线程到齐后,交换数据和计算结果,在进行下一次迭代。

16 并发包的基石

16.2 显示锁

相比synchronized,显示锁支持以非阻塞方式获取锁,可以响应中断,可以限时。