一、关于并发的相关概念

1. 多线程、并发、并行、

  • 并发:多个线程在同一时间段内同时执行。

  • 并行:多个线程在同一时刻同时时执行。

​ 通常一个处理器(或一个核)同一时刻只能处理一个线程,而线程在某些时候可能不需要cpu,在此时可以让cpu去执行其他地任务。为了充分地利用计算机资源,可以创建多个线程,在线程不需要cpu时,转而去执行其他的线程。

​ 然而多线程会带来额外的开销:上下文切换线程调度

2. 什么是上下文切换

​ 当cpu从一个线程切换为另一个线程时,在切换前需要保存上一个线程的状态,切换后需要恢复下一个线程的状态。所以任务(线程)从保存到加载的过程就是一次上下文切换。

如何减少上下文切换

  • 无锁并发编程:多线程竞争锁,会引起上下文切换(挂起)
  • CAS算法:避免加锁
  • 使用最少线程:
  • 协程:单线程实现多任务的调度

3. 死锁

产生死锁的条件

  • 资源互斥:一个资源最多只能由一个线程获得
  • 请求保持:线程在请求其他资源时,不放弃已获得资源
  • 不可剥夺:一个线程获得的资源无法被其他线程剥夺
  • 环路等待: 资源的请求构成了请求环路

如何避免死锁

  • 避免一个线程同时获取多个锁
  • 使用定时锁

3. 多线程一定快吗

​ 在一些情形下,多线程并不一定比单线程快:

  • 任务量比较小,单线程在很短的时间内就能完成
  • 资源限制,如网速、硬盘读写速度、数据库连接池

二、Java并发机制的底层原理

1. volatile

​ 被volatile声明的变量,在修改时会发生两件事:

  • 当前处理器缓存行的数据写回到系统内存
  • 将其他CPU里缓存了该内存地址的数据置为无效

2. Synchronized

​ Java中所有的对象都可以作为锁(通过对象头标记),具体表现如下:

  • Synchronized作用于普通方法:锁是当前对象

  • Synchronized作用于静态方法:锁是当前类的Class对象

  • Synchronized作用于方法快:锁是括号中的对象

    Java引入了偏向锁和轻量级锁,来减少获得锁和释放锁带来的性能损耗。锁一共有四章状态:无锁、偏向锁、轻量级锁、重量级锁。这几个状态,会随着竞争情况逐渐升级,锁可以升级但不能降级。

偏向锁

​ 很多情况下,一个锁是总是由一个线程多次获得。获得偏向锁的线程再次获得该锁,可以直接获得,无需加锁和解锁。

轻量级锁

​ 在获得锁时,自旋一定时间多次获得。

​ 竞争的线程不会被阻塞,适用于同步块可以在很快。

重量级锁

3. 原子操作

​ Java实现原子操作可以通过循环CAS的方式实现。

CAS带来的问题

  • ABA问题
  • 循环带来额外开销:
  • 只能保证一个变量的原子性: