一.锁的划分
从机制来说,锁有6种。
1.自旋锁
是指当一个线程在获取锁的时候,如果锁已经被其他线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取锁才会退出循环。
2.乐观锁
假定没有冲突,在修改数据时候如果发现数据和之前获取的不一样,则读取最新数据,修改后重试。
3.悲观锁
假定会发生冲突,同步所有对数据的相关操作,从读数据就开始上锁。
4.独享锁(写)
给资源加上写锁,线程可以修改资源,其他线程不能再加锁(单写)
5.共享锁(读)
给资源加上读锁后只能读不能改,其他线程也是只能加读锁,不能加写锁(多读)
6.可重入锁、不可重入锁
线程拿到一把锁后,是否可以自由进入同一把锁所同步的其他代码
从实现方式来说,锁有两大类。
一是由 JVM 实现的 synchronized,是可重入锁,独享锁,悲观锁。
二是JDK 提供的 Lock。
Lock是一个接口,实现一般是两种。
1.1ReentrantLock
在功能上可以说跟synchronized非常接近,需要配合condition来控制线程同步的流程
1.2ReadWriteLock
在读的时候是共享锁,在写的时候是独享锁。
有一种单独拿出来说的就是CAS,它是基于硬件实现的一种指令,无需我们手动实现,属于乐观锁.
从使用场景去划分
1.读多写少的场景
cas和readwriteLock
2.读写比较均匀的场景
synchronized和reentrantLock
二.一些注意事项
线程suspend/resume被弃用的原因:suspend不会释放锁,所以很容易导致死锁,而且suspend一定要再resume前面执行,否则也会死锁
wait/notify:如果在notify被调用后,才执行wait方法,线程会一直在wait的状态;wait/notify方法必须在同步块里面使用
park/unpark:不用看顺序,他们就像标志位一样,unpark就好像是“”许可“”线程继续执行,但这个也不释放锁
在循环中检查等待条件,不要用if,因为有伪唤醒的问题
线程池:线程池管理器 工作线程 任务接口 任务队列
线程池策略:1.先判断任务是否达到核心线程数量 2.达到后判断工作队列是否满,所以如果队列是无限容量的,那它肯定不会创建新的线程 3.满了后再判断是否到达最大线程数量,这里才会创建新的线程
4.到最后就会拒绝执行
确定合适的线程数量
1.计算型的任务:cpu数量的1-2倍
2.io型任务:如tomcat默认是200,可以在最小数量和最大数量设置自动增减