面试必问并发安全问题

94 阅读2分钟

线程安全性

什么是线程安全性?我们所写的代码在并发场景下,总是正确的。反之,线程不安全的代码,表现是不可预知的,有可能正确,大多数情况下是错误的。

实现

线程封闭

实现好的并发是一件困难的事情,所有很多时候我们想要躲避并发。避免并发最简单的方法就是线程封闭。

实现

把对象封装到一个线程里,只有这一个线程看到此对象

方式

栈封闭

简单说就是局部变量,线程都有自己独立的线程栈。

ThreadLocal

每个线程都有独立的副本,注意泄漏问题。

无状态的类

往往是工具类,没有任何成员变量的类,一定是线程安全的类,这种类一定是线程安全的。 即使传入的是个对象,这个对象本身是不安全的,这种情况也只是传入的参本身是不安全的,跟本类无关,想要保证线程安全,就要保证传入的参本身是安全的。

让类不可变

final修饰,但是要注意,一旦类的成员变量中有对象,final保证不可变并不能保证类的安全性,在多线程下,虽然对象引用不变,但是对象在堆上的实例可能被多个对象同时修改。要保证修饰的类本身也是线程安全的才行。所以说final只保证引用不可变,引用的实例可以随意变化的

加锁和CAS

如使用synchronized关键字,使用显示锁和各种原子变量,修改数据使用CAS机制等

死锁

是指两个或两个以上的进程在执行过程中,由于竞争资源或由于彼此通信造成一种阻塞的现象 发生比较隐蔽,可以通过jstack查看死锁情况

必要条件

  • 互斥条件
  • 请求和保持条件
  • 不剥夺条件
  • 环路等待条件

解决

  • 单线程
  • 多线程有序性
  • 显示锁trylock机制 示例:

image.png 在该例子中细心的你发现最后执行了一个线程休眠方法而且是随机的,目的是避免活锁的发生

其他线程安全问题

活锁

代码不严谨,导致线程处于运行态,但没有走业务,而是不停获取锁资源

线程饥饿

低优先级的线程,总是拿不到执行时间

线程安全的单例模式

双重检查锁定

单例模式推荐实现

  • 懒汉式
  • 饿汉式