Android面试 - 线程安全

152 阅读4分钟

线程安全:

  • 原子性:一个操作要么都执行要么都不执行。
  • 可见性:当一个线程修改了共享变量的值,其他线程能够立即得知这个修改。
  • 有序性:程序代码按照指令顺序执行。

CAS

  • Compare And Swap 比较并交换
  • 内存地址的值V,旧的预期的值A,要修改的新值B。只要当V与A相等时,才会将V修改为新的值B。
  • Unsafe将cas编译成一条cpu指令,没有函数调用
  • aba问题:当前值可能是变为b后再变为a,此a非彼a,通过加版本号能解决
  • 非阻塞同步:没有挂起唤醒操作,多个线程同时修改一个共享变量时,只有一个线程会成功,其余失败,它们可以选择轮询。

线程状态:

New(新建)

  • 新建,一个线程还没开始执行的状态。
  • 新创建的线程,还没有开始执行,也就是还没有调用strat()方法。

Runnable(可运行)

  • 正在运行,一个线程在Java虚拟机中正在运行的状态。
  • 正在运行的状态不等于CPU正在运行这个线程的代码,也可能正在等待操作系统的资源,比如cpu资源。 当cpu空闲下来时,将会选择一个处于 RUNNABLE 状态的线程执行。

Blocking(阻塞)

  • 阻塞,线程阻塞表示正在等待获取监控锁时的状态。
  • 此时线程正在等待获取监控锁,以进入一个同步代码块或同步方法,或是在调用了Object.wait() 方法后重新进入一个同步代码块或同步方法。

TimeWaiting(限期等待)

限时等待,线程处于有限时间的等待状态,直到另一个线程执行特定的操作或到时间,将会脱离此状态。线程处于限时等待状态,是由于线程调用了一下方法之一,并且指定一个正数等待时间。

  • Thread.sleep()
  • 带超时时间参数的 Object.wait()
  • 带超时时间参数的 Thread.join()
  • LockSupport.parkNanos
  • LockSupport.parkUntil

Waiting(无限期等待)

等待,线程处于无限期的等待状态,直到另一个线程执行特定的操作才能摆脱此状态。线程处于等待状态,是由于线程调用了以下方法之一。

  • 不带超时时间参数的 Object.wait()
  • 不带超时时间参数的 Thread.join()
  • LockSupport.park() 线程处于等待状态时,正在等待另一个线程执行特定操作。

Terminated(终止)

终止,已经退出了的线程在此状态。终止状态,表示线程已经执行完毕了。

ReentrantLock和synchronized的区别

  • 等待可中断:ReentrantLock当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情。
  • 公平锁:ReentrantLock默认情况下也是非公平的,但可以通过带布尔值true的构造函数改用公平锁。
  • 锁绑定多个条件:一个ReentrantLock对象可以通过多次调用newCondition()同时绑定多个Condition对象。

synchronized和volatile的区别

  • synchronized能保证操作的原子性,而volatile不可以,假设线程A和线程B同时读取到变量a值,A修改a后将值更新到主内存,同时B也修改a值会覆盖A的修改操作
  • synchronized可修饰变量、方法和类,而volatile只能修饰变量
  • synchronized可能会造成线程阻塞,而volatile不会造成线程的阻塞

线程池

  • 复用线程池中的线程,避免线程的创建和销毁带来的性能消耗;
  • 有效控制线程池的最大并发数,避免大量的线程之间因互相抢占系统资源而导致阻塞现象;
  • 进行线程管理,提供定时 / 循环间隔执行等功能

线程池重要参数

  1. 核心线程数(coolPoolSize): 即使处于空闲状态,也会被保留下来的线程,会一直存活。
  2. 最大线程数(maximumPoolSize): 核心线程数 + 非核心线程数。控制可以创建的线程的数量。
  3. 线程存活保持时间(keepAliveTime):非核心线程超时时间,超过这个时长,闲置的非核心线程就会被回收。
  4. 任务队列(workQueue):用于传输和保存等待执行任务的阻塞队列。
  5. 线程工厂(threadFactory):可创建新线程。
  6. 线程饱和策略(handler):当线程池和队列都满了,再加入线程会执行此策略。

死锁

四个必要条件

  1. 互斥访问资源
  2. 资源只能主动释放,不会被剥夺
  3. 持有资源并且还请求资源
  4. 循环等待 解决方案是:加锁顺序+超时放弃