文章目录
- 1 什么是进程?什么是线程?进程和线程的区别(从java语言的角度来讲解)?
- 2 说数并发和并行的区别?
- 3 为什么要使用多线程呢?
- 4 使用多线程能带来什么问题?临界资源?
- 5 说说线程的生命周期?
- 6 线程状态阻塞和等待状态的区别?
- 7 JMM的作用是什么?
- 8 as-if -serial是什么?
- 9 heppens-before是什么?
- 10 什么是指令重排序?
- 11 as-if-serial 和happens-before有什么区别?
- 12 程序计数器为什么是线程私有的?
- 13 虚拟机栈为什么是线程私有的?
- 14 用一句话简单说下堆和方法区
- 15 wait和sleep()的区别?
- 16 为什么我们开启线程要使用start 方法,而不能使用run方法?
- 17 谈一谈volatile
- 18 final可以确保可见性吗?
- 19 谈一谈synchronized
- 20 锁优化有哪些策略?
- 21 自旋锁是什么?
- 22 什么是自适应自旋锁?
- 23 什么是锁消除?
- 24 锁粗化是什么?
- 25 偏向锁是什么?
- 26 轻量级锁是什么?
- 27 偏向锁,轻量级锁和重量级锁的区别?
- 28 Lock 和 synchronized有什么区别?
- 29 AQS了解吗?
- 30 AQS有哪两种模式?
- 31 AQS独占式获取锁和释放锁的原理?
- 32 AQS共享式获取锁和释放锁的原理?
- 33 为什么只有前驱节点是头节点时才能尝试获取同步状态?
- 34 线程创建的方式有哪些?
- 35 通过继承Thread类方式创建线程和实现Runnable接口创建线程的区别?
- 36 什么是守护线程?
- 37 线程通信的方式有哪些?
- 38 使用线程池有什么好处?
- 39 创建线程池的参数有哪些?
- 40 线程池中常见的拒绝策略?
- 41 创建线程池的常用方法
- 41 execute和submit的区别?
- 42 如何关闭线程池?
- 43 线程池的选择策略有什么?
- 44 阻塞队列有哪些选择?
- 45 线程池为什么要适用阻塞队列而不使用非阻塞队列?
- 46 什么是CAS?
- 47 使用CAS的弊端?
- 48 有哪些原子类?
- 49 AtomicIntger 实现原子更新的原理是什么?
- 50 CountDownLatch 是什么?
- 51 CyclicBarrier 是什么?
- 52 Semaphore是什么?
- 53 Exchanger是什么?
- 54 谈一谈你知道的ThreadLocal?
- 55 ThreadLocal存在的问题?
1 什么是进程?什么是线程?进程和线程的区别(从java语言的角度来讲解)?
- 进程是一个具有一定独立功能的程序在一个数据集合上依次动态执行的过程。进程是一个正在执行的程序的实例,包括程序计数器,寄存器,和程序变量的当前值。 进程就是一个程序执行的流程,内部保存程序运行所需要的资源。
- 线程与进程相似,但线程是一个比进程更小的执行单位。一个进程在其执行的过程中可以产生多个线程。与进程不同的是同类的多个线程共享进程的堆和方法区的资源,但每个线程有自己的程序计数器,虚拟机栈,和本地方法区,所以系统在产生一个线程,或是各个线程之间切换工作的时,负担比进程要小的多,也正是因为如此,线程也被称为轻量级进程。线程是进程划分为更小的运行单位。线程和进程的最大的不同在于基本上各进程是独立的,而各线程不一定,因为同一进程中的线程很有可能会相互影响。线程执行开销小,但是不利于资源的管理和保护,而进程正相反。
- 在java中,我们启动main函数的时候就是启动了一个jvm进程,而main函数所在的线程就是这个进程中的一个线程,也被称为主线程
2 说数并发和并行的区别?
- 并发:同一时间段,多个任务都在执行(单位时间内不一定同时执行)
- 并行:单位时间内,多个任务同时执行
3 为什么要使用多线程呢?
- 提高cpu和Io的利用率。线程间切换和调度成本远远小于进程。这个减少了进程上下文切换的开销
- 在硬件和软件之间达到一个平衡点
- 利用好多线程能够提高系统整体并发能力以及性能
4 使用多线程能带来什么问题?临界资源?
线程死锁,内存泄漏
5 说说线程的生命周期?
- New 初始状态,线程被构建,但是还没有调用start()方法
- Runnable运行状态,java线程把操作系统中的线程就绪和运行都被称为“运行中”
- Blocked 阻塞状态,线程在等待monitor lock,等待进入synchronzied方法,等待进入synchrinzed块
- Waiting 一个线程等待另外一个线程的特定操作,表示线程进入等待状态,进入该状态表示当前线程需要等待其他线程做出一些特定的动作(通知或中断),在java中调用了wait(),jion(),park()方法,不带time参数的
- Time_Waiting 超时等待状态,该状态下不同于waiting状态,它是可以自己在指定时间了自行恢复到Runnable状态,Thread.sleep(long),Object.wait(long),thread.join(long),Locksupport.parkUntil(),Locksupport.parkNanos()
- Terminated 表示线程进入终止状态
6 线程状态阻塞和等待状态的区别?
Blocked状态是线程等待moniter锁的时候出现的,不是lock锁,这个状态不由其他线程控制,由jvm控制唤醒,不是由其他线程唤醒
Waiting状态表示这个线程,要等待其他线程做出一些特定的动作如通知和中断等等。
7 JMM的作用是什么?
8 as-if -serial是什么?
as-if-serialb保证不管代码被如何重排序,单线程程序的执行结果不能改变,编译器和处理器必须遵循as-if-serial语义,为了遵循as-if-serial,编译器和处理器不会对存在数据依赖关系的操作重排序,因为这种重排序会改变程序结果。如果操作之间不存在数据依赖关系,这些操作就可以被编译器和处理器重排序,as-if-serial把单线程程序保护起来,给程序员一种错觉:单线程程序是按程序的顺序执行的。
一句话说就是:as-if-serial保证不管怎么重排序,单线程程序的执行结果不能改变。
9 heppens-before是什么?
先行发生原则,JMM定义的两项操作间的偏序关系,是判断数据是否存在竞争的重要手段。JMM将happens-before要求禁止的重排序按是否会改变程序执行结果分为两类。对于会改变执行结果的JMM要求编译器和处理器必须禁止,对于不会改变结果的重排序,JMM部做要求。JMM 存在一些天然的 happens-before 关系,无需任何同步器协助就已经存在。如果两个操作的关系不在此列,并且无法从这些规则推导出来,它们就没有顺序性保障,虚拟机可以对它们随意进行重排序。
程序次序规则:一个线程内写在前面的操作先行发生于后面的操作
管程锁定规则:unlock操作先行于后面对同一个锁的操作lock操作
volatile规则:对volatile变量的写操作先行发生于后面的读操作
线程启动规则:线程的start方法先行于线程的每一个操作
线程终止规则:线程中所有操作先行发生于对线程的终止检测
对象终结规则:对象的初始化先于发生finalize方法。
传递性:如果操作A先行于操作B,操作B先行于操作C,那么操作A先行于操作C
一句话说就是:happend-before保证多线程情况下,程序执行的结果是正确的。
10 什么是指令重排序?
为了提高性能,编译器和处理器通常会对指令进行重排序。
11 as-if-serial 和happens-before有什么区别?
as-if-serial保证单线程程序的执行结果不变,happens-before保证正确同步的多线程程序的执行结果不变。这两种语义的目的都是为了在不改变程序执行结果的前提下尽可能提高程序执行并行度。
12 程序计数器为什么是线程私有的?
- 程序计数器中保存了程序运行到了那一条语句,字节码解释器通过改变程序计数器来一次读取指令,控制代码的执行流程
- 在多线程的环境下,程序计数器用于记录当前线程的执行位置,保证线程切换后线程能恢复到正确的执行位置。
13 虚拟机栈为什么是线程私有的?
- 虚拟机栈中保存了每个线程的执行时创建一个存储局部变量表,操作数栈,常量池引用等信息。从方法调用直至执行完成的过程,就对应着一个栈帧在java虚拟机中入栈和出栈的过程。
- 保证线程中的局部变量不会被其他线程访问到,保证线程安全
14 用一句话简单说下堆和方法区
- 堆和方法区是所有线程共享的资源,其中堆是进程中最大的内存,主要用于存储新创建的对象,方法区,用于存放已被加载的类信息,常量,静态变量和编译器编译后的代码
15 wait和sleep()的区别?
- wait() 和sleep()的最大区别就是wait会释放锁,sleep不释放锁。
- wait是object类方法,sleep()是Threadl类方法。
- wait()不含有时间参数,被调用后线程不会自动苏醒
16 为什么我们开启线程要使用start 方法,而不能使用run方法?
- start会创建一个线程,里面也会调用到run方法,而直接运行run()方法,会把run方法当成调用这个方法线程下的一个普通方法来执行。
17 谈一谈volatile
- volatile主要修饰变量,确保了可见性,可有序性
18 final可以确保可见性吗?
final 可以保证可见性,被 final 修饰的字段在构造方法中一旦被初始化完成,并且构造方法没有把 this 引用传递出去,在其他线程中就能看见 final 字段值。
在旧的 JMM 中,一个严重缺陷是线程可能看到 final 值改变。比如一个线程看到一个 int 类型 final 值为 0,此时该值是未初始化前的零值,一段时间后该值被某线程初始化,再去读这个 final 值会发现值变为 1。
为修复该漏洞,JSR-133 为 final 域增加重排序规则:只要对象是正确构造的(被构造对象的引用在构造方法中没有逸出),那么不需要使用同步就可以保证任意线程都能看到这个 final 域初始化后的值。
写 final 域重排序规则
禁止把 final 域的写重排序到构造方法之外,编译器会在 final 域的写后,构造方法的 return 前,插入一个 Store Store 屏障。确保在对象引用为任意线程可见之前,对象的 final 域已经初始化过。
读 final 域重排序规则
在一个线程中,初次读对象引用和初次读该对象包含的 final 域,JMM 禁止处理器重排序这两个操作。编译器在读 final 域操作的前面插入一个 Load Load 屏障,确保在读一个对象的 final 域前一定会先读包含这个 final 域的对象引用。
19 谈一谈synchronized
每个 Java 对象都有一个关联的 monitor,使用 synchronized 时 JVM 会根据使用环境找到对象的 monitor,根据 monitor 的状态进行加解锁的判断。
如果成功加锁就成为该 monitor 的唯一持有者,monitor 在被释放前不能再被其他线程获取。
同步代码块使用 monitorenter 和 monitorexit 这两个字节码指令获取和释放 monitor。这两个字节码指令都需要一个引用类型的参数指明要锁定和解锁的对象,对于同步普通方法,锁是当前实例对象;对于静态同步方法,锁是当前类的 Class 对