文章目录

    • 1、什么是并行?什么是并发?
    • 2、什么是进程?什么是线程?
    • 3、线程生命周期有哪些?状态切换的过程?
    • 4、什么是死锁?死锁产生的条件?如何避免死锁?
    • 5、synchronized锁住的是什么?
    • 6、synchronized底层?
    • 7、synchronized锁升级?
    • 8、volatile关键字的作用?什么是内存可见性?什么是指令重排?volatile禁止指令重排原理?

1、什么是并行?什么是并发?

从操作系统来看,线程是CPU分配的最小单位

  • 并行:就是同一时刻,两个线程都在执行,这就要求有两个CPU去分别执行了,如果只有一个CPU的话,执行完一个线程,才能去执行另一个
  • 并发:就是同一时刻,只有一个执行,但是一个时间段,两个线程都执行了,并发的实现依赖于CPU的切换,切换的时间特别短,基本来说对用户是无感知的

2、什么是进程?什么是线程?

  • 进程:进程是代码在数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位
  • 线程:线程是进程的一个执行路径,一个进程中至少有一个线程,进程中的多个线程共享进程的资源,是CPU分配的基本单位

比如在Java中,启动一个main函数就是启动了一个JVM进程,而这个main就是这个进程中的一个线程(主线程),一个进程中可以有多个线程,他们共享进程的堆和方法区资源,但是每个线程都有自己的程序计数器和栈。

3、线程生命周期有哪些?状态切换的过程?

  • 新生(NEW)
  • 运行(RUNABLE)
  • 阻塞(BLOCKED)
  • 等待(WAITING)
  • 超时等待(TIMED_WAITING)
  • 终止(TERMINATED)

4、什么是死锁?死锁产生的条件?如何避免死锁?

比如线程A持有着资源1,线程B持有着资源2,他们都没有释放各自的资源,而且想要获取对方的资源,所以这两个线程就会互相等待而造成的状态,就称之为死锁。理解如下代码。

public class DeadLockDemo {private static Object resource1 = new Object();//资源 1private static Object resource2 = new Object();//资源 2public static void main(String[] args) {new Thread(() -> {synchronized (resource1) {System.out.println(Thread.currentThread() + "get resource1");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread() + "waiting get resource2");synchronized (resource2) {System.out.println(Thread.currentThread() + "get resource2");}}}, "线程 1").start();new Thread(() -> {synchronized (resource2) {System.out.println(Thread.currentThread() + "get resource2");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread() + "waiting get resource1");synchronized (resource1) {System.out.println(Thread.currentThread() + "get resource1");}}}, "线程 2").start();}}## 输出Thread[线程 1,5,main]get resource1Thread[线程 2,5,main]get resource2Thread[线程 1,5,main]waiting get resource2Thread[线程 2,5,main]waiting get resource1

死锁产生的条件:

  • 互斥:该资源任意一个时刻只由一个线程占用
  • 请求并持有条件:一个线程因请求资源而阻塞时,对已获得的资源保持不放
  • 不剥夺条件:线程已获得的资源在未使用完之前不能被其他线程强行剥夺,只有自己使用完毕后才释放资源
  • 循环等待条件:若干线程之间形成一种头尾相接的循环等待资源关系

如何避免死锁:

至少要破坏死锁发生的一个条件

  • 破坏请求并持有条件:可以一次性请求所有的资源
  • 破坏不可剥夺条件:占用部分资源的线程申请其他的资源,如果申请不到,可以主动释放它占有的资源
  • 破坏循环等待条件:可以在申请资源的时候,按序申请先申请序号小的,再申请序号大的

5、synchronized锁住的是什么?

synchronized本身并不是锁,锁本身是一个对象,synchronized最多相当于“加锁”操作,所以synchronized并不是锁住代码块。用在实例方法上锁的是调用该方法的对象,用在静态方法上,锁的是当前类的所有对象,用在代码块上如果修饰的是对象则锁的是对象,如果修饰的是类,那么锁的是该类的所有对象。

6、synchronized底层?

synchronized 关键字底层原理属于 JVM 层面的东西。

  1. synchronized同步语句块的情况

synchronized同步语句块的实现是使用的是monitorentermonitorexit指令,其中monitorenter指向的是同步代码块开始的位置,也就是执行它的时候,如果锁的计数器为0则表示可以被获取,那么锁的计数器就会加1,其他想要获取锁的看到为1就等待;monitorexit指向的同步代码块结束的位置,执行它就会释放刚才获取的锁,将锁的计数器减1。

  1. synchronized修饰方法的情况

修饰方法的时候,使用的是ACC_SYNCHRONIZED标识,指明了该方法是一个同步方法,JVM通过该标识来辨别一个方法是否声明为同步方法,从而执行响应的同步调用。**

7、synchronized锁升级?

Java对象头里,有一块结构,叫Mark Word标记字段,这块结构会随着锁的状态变化而变化。首先要了解一下锁的四种状态:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态,它们会随着竞争的激烈而逐渐升级,锁可以升级但是不能降级,这种策略是为了提高获得锁和释放锁的效率。

jdk1.6之前,synchronized的实现直接调用了ObjectMonitor的enter和exit,这种锁被称之为重量级锁,为了对所进行优化,jdk1.6对锁的实现引入了大量的优化,如偏向锁、轻量级锁、自旋锁、适应性自旋锁、锁消除、锁粗化等技术来减少锁操作的开销

8、volatile关键字的作用?什么是内存可见性?什么是指令重排?volatile禁止指令重排原理?

  1. 被volatile修饰的共享变量,主要具有了可见性、有序性和不保证原子性。
  2. 内存可见性:这里要谈到Java的内存模型(JMM),每个线程都有格子的工作内存,当修改一些变量的时候,会将新值同步到主内存中,可见性是指当一个线程修改了共享变量的值,其他线程能够立即得知这个修改。
  3. 什么是指令重排,看下面的例子:
public class test1 {static int x = 0;static int b = 0;public static void main(String[] args) {new Thread(() -> {x = b;b = 1;System.out.println("x = " + x + " b = " + b);}).start();new Thread(() -> {x = b;b = 1;System.out.println("x = " + x + " b = " + b);}).start();}}# 结果(多线程情况下)x = 0 b = 1x = 1 b = 1

正经的定义:CPU和编译器为了提升程序执行的效率,会按照一定的规则允许进行指令优化。但代码逻辑之间是存在一定的先后顺序,并发执行时按照不同的执行逻辑会得到不同的结果。

  1. volatile禁止指令重排原理,通过施加内存屏障,禁止指令重排的操作