面试宝典--多线程 09.07

文章目录

      • 1.并发一定快吗
      • 2.并发和并行的区别
      • 3.死锁
      • 4.volatile ⭐
          • volatile原理
          • volatile使用,能不能保证线程安全
      • 5.synchronized
          • synchronized 原理⭐
          • 锁的升级⭐
      • 6.CAS
          • 什么是CAS
          • CAS示例
          • CAS的问题
      • 7.线程池参数⭐
      • 8.线程池流程⭐
      • 9.什么是内存屏障
      • 10.内存屏障解决了我们什么问题
      • 11.什么是重排序
      • 12.happens-before 和as-if-serial
          • happens-before
          • as-if-serial
      • 锁的内存语义
      • JUC包的实现 以及原理
      • 双重检查锁定实现单例⭐
      • 静态内部类实现单例⭐
      • 线程
    • 线程 协程 进程 ⭐
      • 线程的几种状态 以及相互间的转化⭐
      • 等待通知机制⭐
      • thread.join
      • ThreadLocal⭐

1.并发一定快吗

答:不一定
解释:因为线程有创建和上下文切换的开销,当并发执行累加操作不超过百万次时,速度会比并行执行累加操作慢

2.并发和并行的区别

并行表示两件事同时都在做,
并发表示两件事在某个时刻只有一个事情在做.

3.死锁

死锁产生的四个条件

  • 互斥访问
  • 请求与保持
  • 不可剥夺
  • 循环等待

避免死锁的方法

  • 避免一个线程同时获取多个锁
  • 避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源
  • 尝试使用定时锁,使用lock。tryLock(timeout)来替代使用内部锁机制
  • 对于数据库锁,加锁和解锁必须在一个数据库连接里,否则会出现解锁失败的情况

4.volatile ⭐

volatile时轻量级的synchronized,在多处理器开发中保证了共享变量的“可见性”。即当一个线程修改一个共享变量时,另一个线程能读到这个修改的值。

volatile原理

1,通过插入内存屏障指令禁止编译器和CPU对程序进行重排序。

2,当对声明了volatile的变量进行写操作时,JVM就会向处理器发送一条Lock前缀的指令,这条Lock前缀指令产生如下两个作用:
1)Lock前缀指令会引起处理器缓存回写到系统内存,并使用缓存一致性机制来确保回写的原子性。
2)一个处理器的缓存回写到系统内存会导致其他处理器的缓存无效。处理器使用MESI控制协议去维护内部缓存和其他处理器缓存的一致性。处理器能嗅探其他处理器访问系统内存和它们的内部缓存。处理器使用嗅探技术保证它的内部缓存、系统内存和其他处理器的缓存的数据在总线上保持一致。

例如,在Pentium和P6 family处理器中,如果通过嗅探一个处理器来检测其他处理器打算写内存地址,而这个地址当前处于共享状态,那么正在嗅探的处理器将使它的缓存行无效,在下次访问相同内存地址时,强制执行缓存行填充。

volatile使用,能不能保证线程安全

不能
因为volatile不能保证变量操作的原子性,所以试图通过volatile来保证线程安全性是不靠谱的

5.synchronized

synchronized是一个重量级锁,相对于Lock,它会显得那么笨重,以至于我们认为它不是那么的高效而慢慢摒弃它

synchronized 原理⭐
锁的升级⭐

为了减少获得锁和释放锁带来的性能消耗,引入了“偏向锁”和“轻量级锁”
锁一共有四种状态,级别从低到高依次是:无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态

锁可以升级但不能降级,目的是为了提高获取锁和释放锁的效率

6.CAS

什么是CAS

cas是另一个无锁解决方案,更准确的是采用乐观锁技术,实现线程安全的问题。
cas有三个操作数----内存对象(V)、预期原值(A)、新值(B)

CAS原理就是对v对象进行赋值时,先判断原来的值是否为A,如果为A,就把新值B赋值到V对象上面,如果原来的值不是A(代表V的值放生了变化),就不赋新值。

CAS示例
CAS的问题

1、自循环时间长,开销大:自旋CAS如果长时间不成功,会给CPU带来非常大的执行开销。
解决思路:如果JVM能支持处理器提供的pause指令,那么效率会有一定的提升。
pause指令有两个作用:

  • 可以延长流水线执行指令,使COU不会小号过多的执行资源
  • 可以避免在退出循环的时候因内存顺序冲突而引起CPU流水线被清空,从而提高CPU的执行效率

2、只能保证一个共享变量的原子操作:对一个共享变量执行操作时,可以使用循环CAS的方式来保证原子操作。
解决思路:可以用锁;还可以把多个共享变量合并成一个共享变量来操作。

3、ABA问题:如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,实际上发生了变化。
解决思路:使用版本号,从Java1.5开始,JDK的Atomic包里提供了一个类AtomicStampedReference来解决ABA问题

7.线程池参数⭐

  1. corePoolSize(线程池的基本大小):
  2. runnableTaskQueue(任务队列)
  3. maximumPoolSize(线程池最大数量):线程池允许创建的最大线程数
  4. ThreadFactory:用于设置创建线程的工厂
  5. RejectedExecutionHandler(饱和策略)

8.线程池流程⭐

  1. 线程池判断核心线程池里的线程是否都在执行任务,如果不是,则创建一个新的工作线程来执行任务。如果核心线程池里的线程都在执行任务,则进入下一流程
  2. 线程池判断工作队列是否已经满,如果工作队列没有满,则将新提交的任务存储在这个工作队列里,如果工作队列满了,则进入下一个流程
  3. 线程池判断线程池的线程是否都处于工作状态,如果没有,则创建一个新的工作线程来执行任务,如果已经满了,则交给饱和策略来处理这个任务。
    面试宝典--多线程 09.07_第1张图片

9.什么是内存屏障

内存屏障指的是防止指令重排,以及内存可见性的效果。在防止指令重排的时候使用的lock addl指令,这个指令可以防止在此之间的命令不可以指令重排,禁止cpu对其指令优化,内存可见性一般使用的缓存一致性协议,例如mesi协议,这个使用的是窥探的技术,让其他线程中此变量的复制无效化,让其直接在访问内存中的变量,这样就可以保证内存可见性。但是,mesi协议由于使用额的是窥探的技术,所以会造成总线风暴。

10.内存屏障解决了我们什么问题

11.什么是重排序

重排序是指编译器和处理器为了优化程序性能而对指令序列进行重新排序的一种手段。

12.happens-before 和as-if-serial

happens-before

Java使用新的内存模型,它是JMM最核心的概念,使用happens-before的概念来阐述操作之间的内存可见性。在JMM中,如果一个操作执行的结果需要对另一个操作可见,那么这两个操作之间必须要存在happens-before关系,这里提到的两个操作既可以是在一个线程之内,也可以是在不同线程之间。

  • happens-before的定义:
  1. 如果一个操作happens-before另一个操作,那么第一个操作的执行结果将对第二个操作可见,而且第一个操作的执行顺序排在第二个操作之前。
  2. 两个操作之间存在happens-before关系,并不意味着Java平台的具体实现必须按照happens-before关系指定的顺序来执行。如果重排序后的结果与按happens-before关系来的结果一直,那这种重排序并不非法。
  • happens-before规则如下:
  1. 程序顺序规则:一个线程中的每一个操作,happens-before于该线程中的任意后续操作。
  2. 监视器锁规则:对一个锁的解锁,happens-before于随后对这个锁的加锁。
  3. volatile变量规则:对一个volatile域的写,happens-before于任意后续对这个volatile域的读。
  4. 传递性:如果A happens-before B,且B happens-before C,那么A happens-before C。
  5. start()规则:如果线程A执行操作ThreadB.start()并成功返回,那么线程A中的ThreadB.start()操作happens-before于线程B中的任意操作。
  6. join()规则:如果线程A执行操作ThreadB.join()并成功返回,那么线程B中的任意操作happens-before于线程A从ThreadB.join()操作成功返回。
as-if-serial

as-if-serial语义的意思是:不管怎么重排序,单线程程序的执行结果不能被改变。编译器、runtime和处理器都必须遵守as-if-serial语义。所以编译器和处理器不会对存在数据依赖关系的操作做重排序,因为这种重排序会改变执行结果。但是,如果操作之间不存在数据依赖关系,这些操作就可能被编译器和处理器重排序。

锁的内存语义

JUC包的实现 以及原理

我们的volatile内存语义和锁的内存语义和final域的内存语义从程序员和编译器,处理器两个方面来讲最终目的是神秘

双重检查锁定实现单例⭐

静态内部类实现单例⭐

线程

线程 协程 进程 ⭐

线程的几种状态 以及相互间的转化⭐

  1. NEW:初始状态,线程被构建,但是还没有调用start()方法
  2. RUNNABLE:运行状态,Java线程将操作系统中的就绪和运行两种状态笼统地称为“运作中”
  3. BLOCKED:阻塞状态,表示线程阻塞于锁
  4. WAITING:等待状态,表示线程进入等待状态,进入该状态表示当前线程需要等待其他线程做出一些特定动作(通知或中断)
  5. TIME_WAITING:超时等待状态,该状态不同于WAITING,它是可以再指定的时间自行返回的。
  6. TERMINATED:终止状态,表示当前的线程已经执行完毕。

等待通知机制⭐

thread.join

ThreadLocal⭐

你可能感兴趣的:(面试宝典--多线程 09.07)