2021-05-18-12-46-29
并发编程的挑战
1.1 上下文切换
即使是单核处理器也支持多线程执行代码,CPU通过给每个线程分配CPU时间片来实现这个机制。时间片是CPU分配给各个线程的时间,因为时间片非常短,所以CPU通过不停地切换线程执行,让我们感觉多个线程是同时执行的,时间片一般是几十毫秒(ms)。
CPU通过时间片分配算法来循环执行任务,当前任务执行一个时间片后会切换到下一个任务。但是,在切换前会保存上一个任务的状态,以便下次切换回这个任务时,可以再加载这个任务的状态。所以任务从保存到再加载的过程就是一次上下文切换。
1.2 死锁
预防死锁的建议
- 避免一个线程获取多个锁
- 避免一个线程同时占用多个资源,尽量一个锁只占有一个资源
- 尝试使用定时锁,用lock.tryLock(timeout)来替代使用内部锁机制
- 数据库锁加锁解锁必须在同一个数据库连接里,否则会出现资源解锁失败
并发机制底层原理
2.1 volatile
volatile是轻量级的 synchronized,它在多处理器开发中保证了共享变量的“可见性”。可见性的意思是当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值。它不会引起线程上下文的切换和调度。
2.2 synchronized
synchronized是一个重量级锁,jdk1.6后为了减少获得锁和释放锁带来的性能消耗而引入的偏向锁和轻量级锁,以及锁的存储结构和升级过程。
sychronized实现同步的基础:Java每个对象都可作为锁
- 对于普通方法,锁是当前实例对象
- 对于静态方法,锁是当前类的Class字节码
- 对于同步方法块,锁是sychronized括号里的对象
实现原理
JVM通过进入和退出monitor对象来实现方法同步和代码块同步,两者实现细节不一样,代码块同步使用monitorenter和monitorexit指令实现,而方法同步也可通过这两个指令实现。
monitorenter指令是在编译后插入到同步代码块的开始位置,而monitorexit是插入到方法结束处和异常处,JVM要保证每个monitorenter必须有对应的monitorexit与之配对。任何对象都有一个monitor与之关联,当且一个monitor被持有后,它将处于锁定状态。线程执行到monitorenter指令时,将会尝试获取对象所对应的monitor的所有权,即尝试获得对象的锁。