面试篇-Java并发

157 阅读6分钟

1,什么是线程和进程?线程与进程的关系,区别及优缺点?

进程是一次程序的执行。

执行一次程序包含多个任务,线程是一次任务的执行。

进程是资源分配的最小单位,线程是CPU调度的最小单位

提示:可以从从 JVM ⻆度说进程和线程之间的关系

优缺点:线程执行开销小,但不利于资源的管理和保护;而进程正相反

2,为什么要使用多线程呢?

  • 从计算机⻆度来说主要是为了充分利⽤多核 CPU 的能⼒。
  • 从项⽬⻆度来说主要是为了提升系统的性能

3,说说线程的生命周期和状态?

  • New(初始化状态)
  • Runnable(就绪状态)
  • Running(运行状态)
  • Blocked(阻塞状态)
  • Terminated(终止状态)

4,什么是线程死锁?如何避免死锁?如何预防和避免线程死锁?

两个或两个以上线程,因争抢资源造成互相等待的现象,若无外力作用,则会一直持续下去。

# 死锁代码示例:


import java.util.concurrent.TimeUnit;

public class DeadLockDemo
{
    public static void main(String[] args)
    {
        final Object objectLockA = new Object();
        final Object objectLockB = new Object();

        new Thread(() -> {
            synchronized (objectLockA)
            {
                System.out.println(Thread.currentThread().getName()+"\t"+"自己持有A,希望获得B");
                //暂停几秒钟线程
                try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
                synchronized (objectLockB)
                {
                    System.out.println(Thread.currentThread().getName()+"\t"+"A-------已经获得B");
                }
            }
        },"A").start();

        new Thread(() -> {
            synchronized (objectLockB)
            {
                System.out.println(Thread.currentThread().getName()+"\t"+"自己持有B,希望获得A");
                //暂停几秒钟线程
                try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
                synchronized (objectLockA)
                {
                    System.out.println(Thread.currentThread().getName()+"\t"+"B-------已经获得A");
                }
            }
        },"B").start();

    }
}

死锁的四个必要条件

  • 互斥
  • 请求和保存
  • 不可剥夺
  • 环路等待

死锁预防和打破:破坏死锁的必要条件中的一个或多个

如何检测死锁

  • 命令 jps -l
    jstack 进程编号
  • 图形化 jconsole

5,synchronized 关键字

synchronized 关键字的作⽤和用法

作用:被synchronized修饰的对象或方法任意时刻只能有一个线程访问。

用法:修饰普通方法;修饰静态方法;修饰代码块

synchronized 关键字的底层原理

JDK1.6 之后的 synchronized 关键字底层做了哪些优化。 synchronized 锁升级流程。

synchronized 和 ReentrantLock 的区别。

synchronized是属于原生语法层面的互斥,ReentrantLock是API层面的互斥。

ReentrantLock的粗细度和灵活度要高于synchronized。

synchronized 和 volatile 的区别。

1)volatile本质是告诉JVM当前变量在寄存器中的值是不确定的,需要从主存中读取。synchronized则是锁定当前变量,只有当前线程可以访问该变量,其它线程被阻塞。

2)volatile仅能使用在变量级别,synchronized则可以使用在变量、方法。

3)volatile仅能实现变量修改的可见性,而synchronized则可以保证变量修改的可见性和原子性。

4)volatile不会造成线程阻塞,synchronized会造成线程阻塞。

6,并发编程的三个重要特性

  • 原子性
  • 可见性
  • 有序性

7,JMM 和 happens-before 原则。

JMM理念是屏蔽各种硬件和操作系统的内存访问差异,使java程序在各种平台下都能达到一致的访问效果。

happens-before原则

从JMM设计者的角度来分析,设计JMM需要考虑两个关键因素:

  • 程序员希望内存模型易于理解和编程,最好是不要重排序复合人的思维,是个强内存模型。
  • 编译器和处理器希望内存模型对它们的束缚越小越好,这样可以做更多的优化提高性能。

而happens-before就是上述因素的平衡点。

happens-before之8条

1.次序规则:

前一个操作的结果可以被后续的操作获取。    

2.锁定规则:

一个unLock操作先行发生于后面((这里的“后面”是指时间上的先后))对同一个锁的lock操作;    

3.volatile变量规则:

对一个volatile变量的写操作先行发生于后面对这个变量的读操作。
    
前面的写对后面的读是可见的,这里的“后面”同样是指时间上的先后。    

4.传递规则:

如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出操作A先行发生于操作C;    

5.线程启动规则:

Thread对象的start()方法先行发生于此线程的每一个动作    

6.线程中断规则:

对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生;    

7.线程终止规则:

线程中的所有操作都先行发生于对此线程的终止检    

8.线程终结规则:

一个对象的初始化完成(构造函数执行结束)先行发生于它的finalize()方法的开始    

8,volatile 关键字

特性

  • 可见性
  • 禁重排

禁重排通过内存屏障实现。

JMM就将内存屏障插入策略分为4种

写:
    1.在每个volatile 写操作的前面插入一个StoreStore 屏障
    2.在每个volatile 写操作的后面插入一个StoreLoad屏障
读:
    3.在每个volatile读操作的后面插入一个LoadLoad屏障
    4.在每个volatile读操作的后面插入一个LoadStore 屏障 

9,ThreadLocal 关键字

ThreadLocal为线程提供局部变量。

底层原理

ThreadLocal里面包含ThreadLocalMap,ThreadLocal的set方法,会先检查Map里面有没有以ThreadLocal作为key的数据,没有则创建以当前线程的ThreadLocal做key的map数据。

内存泄露问题

ThreadLocalMap 中使⽤的 key 为 ThreadLocal 的弱引用,⽽ value 是强引用;

ThreadLocal 没有被外部牵引的情况下,在垃圾回收的时候,key 会被清理掉,⽽ value 不会 被清理掉。这样⼀来, ThreadLocalMap 中就会出现key为null的Entry。假如我们不做任何措施 的话,value 永远⽆法被GC 回收,这个时候就可能会产生内存泄露。

使用场景

10,线程池

线程池有哪几种

  • newCachedThreadPool
  • newFixedThreadPool
  • newScheduledThreadPool
  • newSingleThreadExecutor

线程池优缺点

线程池重要参数

  • corePoolSize 核心线程数

  • maximumPoolSize 最大线程数

  • keepAliveTime 空闲线程存活时间

  • unit 时间单位

  • workQueue 工作队列

  • threadFactory 线程工厂

  • handler 拒绝策略

    • CallerRunsPolicy 拒绝任务
    • AbortPolicy 抛弃任务,抛出异常
    • DiscardPolicy 直接抛弃任务
    • DiscardOldestPolicy 抛弃队列中最早的任务,尝试把该任务加入

11,ReentrantLock 和 AQS

ReentrantLock类组合了Sync类,而Sync类继承了**AbstractQueuedSynchronizer(AQS)**类,所以ReentrantLock 实现加锁,解锁是通过AQS实现的。

image.png

  • state表示资源状态,默认为0
  • OwnerThread表示持有资源的线程,默认为null
  • CLH存放等待的线程。

image.png

ReentrantLock加锁,会将 state=1,OwnerThread设置为自己,要是有其他线程来访问,会将线程放入CLH队列。解锁,会将 state=0,OwnerHtread置null,在通知CLH队列的线程。

12,CAS 了解么?原理?什么是 ABA 问题?ABA 问题怎么解决?

比较并交换。

原理:CAS操作有依赖三个值,内存中的值,预估旧值,要修改的新值。内存值和旧值相等,就修改。

ABA问题:一个线程将变量修改后,又改回去,另一个线程对变量操作时,不知道变量被修改过。

解决ABA问题

AtomicStampedReference:AtomicStampedReference在构建的时候需要一个类似int型的变量stamped,每一次需改,stamped都会变化,所以能够解决ABA问题。

13,Atomic 原⼦类