文章目录
如何解决线程并发安全问题?
所有的并发模式在解决线程安全问题时,采用的方案都是**序列化访问临界资源**。即在同一时刻,只能有一个
线程访问临界资源,也称作同步互斥访问。Java 中,提供了两种方式来实现同步互斥访问:synchronized和 Lock
同步器的本质就是加锁
加锁目的:序列化访问临界资源,即同一时刻只能有一个线程访问临界资源(同步互斥访问)不过有一点需要区别
的是:当多个线程执行一个方法时,该方法内部的局部变量并不是临界资源,因为这些局部变量是在每个线程的私有栈中
因此不具有共享性,不会导致线程安全问题。
一、synchronized
1.使用方式
synchronized是对象锁,使用的时候需要传一个对象当成形参。
1、同步实例方法,锁是当前实例对象
2、同步类方法,锁是当前类对象
3、同步代码块,锁是括号里面的对象
使用方式的具体代码,就不记录了
2.Monitor
synchronized关键字被编译成字节码后会被翻译成monitorenter 和 monitorexit 两条指令分别在同步块逻辑代码
的起始位置与结束置。
在使用synchronized的同步方法或同步代码块中,会在方法开始的时候有一个monitorenter指令,在结束的时候有一
个monitorexit指令。
具体Monitor代表什么我也不太清楚,就知道是每个对象都是一个Monitor,是用c++语言写的,这个没去研究了
3.锁升级(自己的白话总结)
1、无锁状态
无锁状态是使用了synchronized关键字的方法,在线程开始时没有用到或者锁已经消除。
2、偏向锁
当一个线程A首先获取到了锁,这个时候锁还没有消除,线程A再次去获取锁,发现此时的持有锁的是线程A,这个时候就
让线程A再次获得锁并将锁升级为偏向锁。当这个线程再次请求锁时,无需再做任何同步操作,即获取锁的过程,这样就省去
了大量有关锁申请的操作,从而也就提供程序的性能。
用处:当一个线程多次获取锁的时候可以减少线程开销。
3、轻量级锁
如果线程A已经获得了锁,线程B获取锁,这时候B肯定会获取锁失败。如果线程A此时刚好要结束,则撤销偏向锁升级为
轻量级锁。在轻量级锁结束前,线程B会进入自旋锁状态,一直去获取锁,就相当于轮询了一定次数还没有获取锁,就停止轮
询。升级为重量级锁。
用处:轻量级锁能够提升程序性能的依据是“对绝大部分的锁,在整个同步周期内都不存在竞争”
4、重量级锁
升级成重量级锁之后,就等A线程释放锁,再唤醒B线程获取锁。
还有一个逃逸分析就不在这个里面记录了
二、ReentrantLock
1.特性
- 阻塞等待队列
- 共享/独占
- 公平/非公平 (sync的两种实现)
- 可重入
- 允许中断(优雅的实现停止线程)
2.数据结构
3.具体实现
我的理解是根据state这个状态值去获取锁,唤醒锁的操作等等等等等等。感觉有好多知识点~
害,还没有总结好,现在只是看了几遍源码,还不能去熟练总结。
总结
AQS很重要,因为阻塞队列用的也是加锁,然后阻塞队列会引入条件队列。后面的线程池、mq、dobbo等中间件都用到了
阻塞队列。也是其它并发工具类的基础。