文章目录
线程和进程
基础概念
- 线程:是CPU调度的最小单位
- 进程:是资源分配的最小单位
线程和进程的区别
- 一个进程中包括多个线程
- 进程消耗的资源比线程消耗的资源较多
- 线程之间的数据可以共享,进程之间的数据没办法共享
- 一个进程挂了不影响其他的进程,但一个线程挂了,可能会影响其他的线程
线程的状态
- 创建:刚开始创建
- 就绪:准备好线程启动的所有事情,就差获取 CPU 的使用权
- 运行:获取到 CPU 的使用权,开始运行
- 堵塞:线程因为某些原因让出 CPU 的使用权,wait
- 结束:线程运行结束
synchronize
修饰的不同
- 修饰方法的话,等同于锁当前的 this
- 修饰静态方法的话,等同于锁当前的 T.Class
不能锁什么
调用方法
- 一个线程在调用加锁的方法的同时也能够调用非加锁的方法
可重入锁
- 对于可重入锁来说,假如有两个方法,m1(),m2(),用 synchronize 修饰
- m1() 方法中嵌套着 m2()
- 当一个线程获取到 m1() 的锁时,可自动进入 m2() 方法,不需要获得 m2() 的锁
为什么 synchronize 是可重入锁呢
- 如果一个类有一个父类
- 子类初始化需要先初始化父类,如果我子类拿到锁,super 父类,如果不是重入锁的话,就有可能发送死锁的现象
当线程出现异常
- 当程序出现异常,会导致锁的释放,其他的线程会疯狂的涌入进来
底层实现原理
- 对象结构: 对象头、实例数据、对齐填充字节
- 对象头:Mark World、指向类的指针、数组长度
- 根据对象头中的 Mark World 的锁标志位,来形成一个锁升级 【无锁 – 偏向锁 – 轻量级锁 – 重量级锁】
流程
- JDK 前期的时候,直接使用的是重量锁,后来进行的锁升级
- synchronize (Object)
- Mark World 记录着 Object 的ID,进入偏向锁,如果下次进来的还是这个 ID,则直接进入,不需要获取锁的过程
- 当下次进来的不是该 ID 时,则进行自旋锁,通过不断的自旋,来等待着锁的释放,CAS While(true)
- 如果自旋了超过10次,则会变成重量锁,通过 OS 的 lock 指令来实现
自旋锁和重量锁的比较
- 如果当前的线程少,时间短,则使用自旋锁
- 如果当前的线程多,时间长,则使用重量锁