多线程安全问题
线程安全问题是指当多个线程同时读写一个共享资源并没有任何同步措施时,导致出现脏数据或其他不可预见的结果的问题。
2、线程安全的实现方法
1)互斥同步
互斥同步是最常见的一种并发正确性保障手段,同步是指在多个线程并发访问共享数据,保证共享数据在同一个时刻只被一个线程使用。而互斥是实现同步的一种手段。
- 互斥:互斥的机制,保证同一时间只有一个线程可以操作共享资源 synchronized,Lock等。
- 临界值:让多线程串行话去访问资源
- 事件通知:通过事件的通知去保证大家都有序访问共享资源
- 信号量:多个任务同时访问,同时限制数量,比如发令枪CDL,Semaphore等
2)非阻塞同步
随着硬件指令集的发展(保证操作和冲突检测具有原子性),可以选择基于冲突检测的乐观并发策略,即先进行操作,如果没有其他线程争用共享数据,那操作就成功了,如果共享数据有争用,产生了冲突,就再采取其他补偿措施(比如不断重试,直到成功为止)。硬件保证一个从语义上看起来需要多次操作的行为只通过一条处理器指令就能完成,这类指令常用的有:
- 测试并设置 test-and-set
- 获取并增加 fetch-and-increment
- 交换 swap
- 比较并交换 CAS
- 加载链接/条件存储
CAS指令需要有3个操作数,分别是内存位置(V),旧的预期值(A)和新值(B),CAS指令执行时,当且仅当V符合预期值A时,处理器就用新值B更新V的值,否则它就不更新,但是无论是否更新V,都会返回V的旧值。该处理过程是一个原子操作。 会有ABA问题,如果需要解决ABA问题,改用互斥同步可能会比原子类更高效。
3)无同步方案
3、volatile 变量使用场景
场景1,运算结果并不依赖变量的当前值,或者能够确保只有单一的线程修改变量的值
场景2,变量不需要与其他的状态变量共同参与不变约束
4、锁优化
HotSpot虚拟机为了在线程之间更高效的共享数据,以及解决竞争问题,提高程序的执行效率,花费大量精力实现锁优化技术,如适应性自旋,锁消除,锁粗化,轻量级锁,偏向锁。
3.1)自旋锁与自适应锁
public class ThreadSafe extends Thread { public void run() { while (!isInterrupted()){ //非阻塞过程中通过判断中断标志来退出 try{ Thread.sleep(5*1000);//阻塞过程捕获中断异常来退出 }catch(InterruptedException e){ e.printStackTrace(); break;//捕获到异常之后,执行 break 跳出循环 } } } }