线程安全与锁优化
我们可以将JAVA中各种操作共享数据分为5类:不可变、绝对线程安全、相对线程安全、线程兼容和线程对立。
- 不可变:在JAVA语言中,不可变对象一定是线程安全的。只要一个不可变对象被正确的构建出来(没有发生this逃逸的情况),那其外部的可见状态永远也不会改变。保证对象行为不影响自己状态途径的途径有很多种,其中最简单的就是把对象中带有状态的变量都声明为final。
- 绝对线程安全:不管运行时环境如何环境如何,调用着都不要额外的同步措施。
- 线程相对安全:这个对象的单独的操作时线程安全的,但是对于一些特定顺序的连续调用,就可能需要在调用端使用额外的同步手段来保证正确性。
- 线程兼容:指对象本生不是线程安全的,但是可以通过在调用端正确的使用同步手段来保证对象在并发环境中可以安全地使用。
- 线程对立:指无论调用端是否采取了同步措施,都无法在多线程环境中并发使用的代码。
线程安全实现的方法
互斥同步:指在多个线程并发访问共享数据时,保证共享数据在同一时刻只被一个(或者是一些)线程使用。Synchronize关键字是互斥同步最基本的手段。ReentrantLock和Synchronize相比,增加了一些高级功能,主要有以下三项:等待可中断、可实现公平锁以及锁可以绑定多个条件。
- 等待可中断:如果获得锁的线程,长时间不释放锁,正在等待的线程可以选择放弃等待,改为处理其他事情。
- 公平锁:当多个线程等待同一个锁时,必须按照申请锁的时间顺序来一次获得锁,可以通过boolean 类型的构造函数使用公平锁。
- 绑定多个条件:ReentrantLock对象可以同时绑定多个Condition对象,而synchronized中,锁对象的wait() 和 notify() 或notifyAll() 方法可以实现一个隐含的条件,如果多余一个条件关联的时候,就不得不额外加个锁,而ReentantLock无需这么做,只需要多次new Condition方法即可。
非阻塞同步:基于冲突检测的乐观并发策略,也就是说,先进行操作,如果没有其他线程争用共享数据,按操作就成功了;如果共享数据有争用,产生了冲突,那就再采取其他的补偿措施,这种乐观的并发策略的许多实现都是不需要把线程挂起的。
synchronizd 工作原理
Java 线程在执行到synchronied 的时候,会形成两个字节码指令,这里相当于是一个监视器(monitor),监控synchronized 保护的区域,监视器会设置几种状态用来区分请求线程:
- Contention List : 所有请求的线程将被首先放置到该竞争队列
- Entry List: Contention List 的那些有资格成为候选人的线程会被移到Entry List
- Wait Set:那些调用wait 方法被阻塞的线程被放置到这里
- OnDeck :任何时刻最多有一个线程正竞争锁,该线程称为OnDeck
- Owner :获得所的线程叫Owner
- !Owner :释放锁的线程
下面是状态的转换关系:
不忘初心,方得始终
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步