java面试——多线程

Posted CS408

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java面试——多线程相关的知识,希望对你有一定的参考价值。

背景:java知识比较宽泛,最好对每一类知识点进行分类总结,方便后面学习查看。该文主要用来总结多线程方面的知识点。

并发与并行的概念

并发性(concurrency)和并行性(parallel)是两个概念——

并行指在同一时刻,有多条指令在多个处理器上同时执行;

并发指在同一时刻只能有一条指令执行,但多个进程指令被快速轮换执行,使得在宏观上具有多个进程同时执行的效果。

出自《疯狂java讲义》第16章 多线程

线程的状态转换

1. 初始(NEW):新创建了一个线程对象,但还没有调用start()方法。
2. 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。
线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。
3. 阻塞(BLOCKED):表示线程阻塞于锁。
4. 等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
5. 超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。
6. 终止(TERMINATED):表示该线程已经执行完毕。

 

 

 Java线程的6种状态及切换(透彻讲解)

ps:初始(new)、运行(runnable)、阻塞(blocked)、等待(waiting)、超时等待(timed_waiting)、终止(terminated)

CyC2018

https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/Java%20%E5%B9%B6%E5%8F%91.md#sleep

sleep()和yield()方法的区别

什么情况下会抛出InterruptedException,为什么会抛出?

Java线程之 InterruptedException 异常

sleep()和wait()方法的最大区别是:
    sleep()睡眠时,保持对象锁,仍然占有该锁;
    而wait()睡眠时,释放对象锁。
  但是wait()和sleep()都可以通过interrupt()方法打断线程的暂停状态,从而使线程立刻抛出InterruptedException(但不建议使用该方法)。

Java中sleep()与wait()区别

CAS与volatile

Java并发之AQS(AbstractQueuedSynchronizer)原理讲解
ps:其中有两个知识点,CAS原理volatile原理讲解的很透彻。

最通俗易懂的乐观锁与悲观锁原理及实现

 

知道这些,面试时volatile就稳了

volatile关键字的理解

volatile是JVM提供的轻量级的同步机制
1.保证可见性
2.保证有序性,禁止指令重排
3.不保证原子性(需要借助synchronized或者CAS)

JMM的内存模型 (ps:看图理解会更好):

每个线程都有自己独立的工作区间,为了匹配CPU的运行速度,他们不会直接从内存中读取数据,而是将数据拷贝一份到CPU缓存中(即每个线程自己的工作内存),他们之间的相互交互,是通过内存来完成的。

根据JMM内存模型的8大原子操作,每个线程在将数据操作完stroe回主存之前,会加lock指令来锁定内存区域的缓存(缓存行锁定),根据MESI缓存一致性协议,总线通过侦听器发现数据被修改,会立即让其他线程工作内存中不一致的副本立即失效。

volatile关键字是怎么保证有序性的?
使用volatile关键字修饰共享变量便可以禁止指令重排序。若用volatile修饰共享变量,在JVM底层volatile是采用“内存屏障”来实现禁止特定类型的处理器重排序。加入volatile关键字时,会多出一个lock前缀指令,lock前缀指令实际上相当于一个内存屏障(也成内存栅栏),内存屏障会提供3个功能:

1.它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成;

2.它会强制将对缓存的修改操作立即写入主存;

3.如果是写操作,它会导致其他CPU中对应的缓存行无效。

JMM具备一些先天的有序性,通过Happens-Before原则就可以保证的一定的有序性。


Java面试官最爱问的volatile关键字

并发编程的3大特性:

原子性、可见性、有序性

happens-before8个规则:
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()(启动线程B) , 那么A线程的ThreadB_start()happens-before 于B中的任意操作
6.join()原则: 如果A执行ThreadB.join()并且成功返回,那么线程B中的任意操作happens-before于线程A从ThreadB.join()操作成功返回。
7.interrupt()原则: 对线程interrupt()方法的调用先行发生于被中断线程代码检测到中断事件的发生,可以通过Thread.interrupted()方法检测是否有中断发生
8.finalize()原则:一个对象的初始化完成先行发生于它的finalize()方法的开始

实际使用volatile 单例模式双重锁

class Singleton{
    private volatile static Singleton instance = null;
 
    private Singleton() {
 
    }
 
    public static Singleton getInstance() {
        if(instance==null) {
            synchronized (Singleton.class) {
                if(instance==null)
                    instance = new Singleton();
            }
        }
        return instance;
    }
}

 

一个单例模式中volatile关键字引发的思考

 为什么单例模式需要加volatile呢?禁止指令重排序

为了防止 instance = new Singleton(); 操作指令重排序,导致另一个线程获得的instance是未初始化的对象 出现空指针

instance = new Singleton(); // 第10行

// 可以分解为以下三个步骤
1 memory=allocate();// 分配内存 相当于c的malloc
2 ctorInstanc(memory) //初始化对象
3 s=memory //设置s指向刚分配的地址

// 上述三个步骤可能会被重排序为 1-3-2,也就是:
1 memory=allocate();// 分配内存 相当于c的malloc
3 s=memory //设置s指向刚分配的地址
2 ctorInstanc(memory) //初始化对象

 

 

 

CountDownLatch、CyclicBarrier和Semaphore

 Java并发编程:CountDownLatch、CyclicBarrier和Semaphore

多线程interrupt()和线程终止方式

Java多线程系列--“基础篇”09之 interrupt()和线程终止方式

线程池的使用和分析

聊聊并发(三)——JAVA 线程池的分析和使用

ps:线程池的工作流程,如何合理配置线程池,线程池的监控

Java 四种线程池的用法分析

ps:线程池的基本用法 会手写

 

(转)Java并发编程:线程池的使用

自己汇总

搞懂 ThreadLocal

Java并发编程:深入剖析ThreadLocal

 

以上是关于java面试——多线程的主要内容,如果未能解决你的问题,请参考以下文章

经验总结:Java高级工程师面试题-字节跳动,成功跳槽阿里!

面试官:Java多线程有哪几种实现方式如何实现?

什么是JAVA的多线程?

Java面试题多线程-线程安全

java面试啥是多线程

面试题: java多线程 !=!=未看