秋招面试突击之-------Java并发篇

秋招面试突击之-------Java并发篇

这里写目录标题

  • 秋招面试突击之-------Java并发篇
    • 一、Java如何开启线程?如何保证线程安全?
    • 二、Volatile和synchornized区别?DCL单例模式为什么要加Volatile?
    • 三、Java线程锁机制?偏向锁、轻量级锁、重量级锁区别?锁机制的升级?
    • 四、AQS,AQS如何实现可重入锁
    • 五、如何保证多个线程同时执行、依次执行、有序交错执行?
    • 六、如何对一个字符串快速进行排序?
    • 七、线程池,拒绝策略

一、Java如何开启线程?如何保证线程安全?

引申问题:进程和线程的区别:进程是操作系统分配资源的最小单位,线程是OS进行任务分配的最小单元,线程隶属于进程;

  • 如可开启线程
    • 继承Thread类,重写run方法,调用start方法
    • 实现Runnable接口。实现run方法
    • 实现callable接口,实现call方法;可以获得线程返回值,通过FutureTask创建一个线程,获取返回值
    • 线程池创建开启线程;必须很熟悉才能说

为什么设计这几种?:JDK考虑单继承多实现的机制,

  • 如何保证线程安全加锁

引申-----什么锁,如何加锁?-----synchronized 和 Lock;----引发出一系列锁的问题;

二、Volatile和synchornized区别?DCL单例模式为什么要加Volatile?

  • volatile:两个点:只能保证可见性和防止指令重排;不保证原子性;

可见性:多个线程访问同一资源变量,该资源变量发生变化后,会触发总线嗅探机制,通过Cpu缓存一致性协议将线程中的内存副本中的缓存行变为失效状态,导致该线程主动重新到主内存load到变量副本中!从而保证可见性;—底层汇编lock指令;

**指令重排:**JVM优化代码的一种手段,通过volatile修饰的变量周围代码段不会发生指令重排,求其原理是底层通过Lock指令添加内存屏障;从而实现防止指令重排; 这也是单例模式加volatile的原因;通过防止 指令重排阻止其他线程读取到半初始化的对象!

指令重排遵从的原则:as-if-serial; happends-before

  • Synchornized: 用来加锁,适用于一个线程写多个线程读。

三、Java线程锁机制?偏向锁、轻量级锁、重量级锁区别?锁机制的升级?

引申–synchornized关键字,通过资源的竞争触发的锁升级操作

锁操作:对象的对象头;锁对象的标记位;通过标记对象的markword;在其他线程读取该对象的markword就可知道该对象的锁状态!状态分为下面几种,无锁(001)

  • 偏向锁(101):少量线程有序访问共享资源,线程获取偏向锁,偏向锁其实没有上锁,只是标记该资源被某一线程占用,如果触发竞争了,竞争的线程获取锁失败,就会升级为轻量级锁;
  • 轻量级锁(00):所谓轻量级锁,就是线程在自旋等待锁的行为,自旋由cpu控制,因此自旋行为消耗cpu,正是因为这样,当自旋线程变多后,cpu的占用率升高,自此升级为重量级锁防止cpu处理这些大量的无意义的自旋行为;亦或者线程自旋次数超过某一个阈值也会膨胀为重量级锁,
  • 重量级锁(10):重量级锁不是JVM来控制,而是上报OS,来处理,OS的处理方式非常霸道,将所有等待线程挂起,将之挂起等待唤醒,从而又带来了CPU上下文切换的开销;效率比较低;在1.6之前都是以这种方式处理;

JVM底层优化机制: -XX:UseBiasedLocking : 是否打开偏向锁;默认不打开;等过了四秒之后new出的对象都加上了偏向锁;

四、AQS,AQS如何实现可重入锁

  • AQS:JDK 提供的线程同步框架;李二狗开发;基于Lock接口实现锁机制; 核心:通过一个state信号量以及双向循环链表;线程排队,通过state信号量规范控制线程排队或放行;在不同的场景下有不同的意义;

  • 可重入锁:同一个线程多次进入同一个加锁的代码段,state表示加锁次数,0表示无锁,加一次锁就+1,释放锁就-1;

五、如何保证多个线程同时执行、依次执行、有序交错执行?

考察并发工具;countdownlatch、cylicBarrier、Semaphore。

countDownlatch:线程同时执行,同一起跑线;(同时执行)

cylicBarrier:定义座位数,等座位站满了就走(同时执行)

Semaphore:给线程分配不同的重要性,定义信号数量,给线程定义权重,线程必须获得相应的信号数量才能执行;(依次执行,交错执行)

六、如何对一个字符串快速进行排序?

含义;考察fork/join框架,分而治之!其实就是归并排序,但是使用并发执行;

  • 拆分Fork:也就是归并排序中的二分数组的过程,不断二分将任务提交到pool中;直到拆分成长度<= 2之后才是进行计算,
  • 汇总Join:归并中的合并阶段,充分调用cpu资源并行实现合并操作!

七、线程池,拒绝策略

  • 线程池创建 :
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue,  ThreadFactory threadFactory,RejectedExecutionHandler handler)
    corePoolSize -- 核心线程容量
    maximumPoolSize -- 最大线程容量
    keepAliveTime -- 线程无任务的存活时间
    unit -- 时间
    workQueue -- 阻塞队列
    threadFactory -- 线程工厂
    handler -- 拒绝策略
  • 执行流程 : 主线程提交任务到线程池,线程池判断是否有核心线程,如果没有就创建,直到核心线程数到达corePoolSize,随后的任务会被放在workQueue阻塞队列中进行排队,核心线程执行完毕后会从队列中获取任务;如果提交任务时队列已经满了,就会创建非核心线程来执行任务,如果队列满了,线程数也满了,就会启动拒绝策略;
  • 核心方法:addWorker方法

你可能感兴趣的:(好好学Java,java,面试,经验分享)