前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >2019/03面试题-并发为主

2019/03面试题-并发为主

作者头像
用户1134788
发布2019-05-25 17:08:43
4060
发布2019-05-25 17:08:43
举报

14日面试题:

问题1:线程类的构造方法,静态代码块是被哪个线程调用的?

解析:

  首先提到了线程类,那么在java中创建(实现)线程是有三种方式的:

  实现interface Runnable的run()  继承class Thread override run()  使用FutureTask的方式(此方式有返回值,但是实际上还是要依赖于Thread类)

  补充FutureTask方式:

代码语言:javascript
复制
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

public class Main {
    public static void main(String[] args) {
        FutureTask<String> futureTask = new FutureTask<>(new CallerTask());
        new Thread(futureTask).start();

        try {
            String result = futureTask.get();
            System.out.println(result);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class CallerTask implements Callable<String> {
    @Override
    public String call() throws Exception {
        return "i am a baby";
    }
}

  综合上面三种方式,结合问题的构造方法,那么答案应该就是Thread的构造方法了。

  提到静态代码块:先找到静态代码块的概念:

  在Java中使用static和{}声明的代码块就是静态代码块。

  查了半天也没找到很明确的指向:看到群友说百度到了:静态代码块是被new这个线程类所在线程所调用的。

  静态代码块是在类加载的时候调用的,且类加载器是缺省规则,那么调用静态代码块的线程就是调用当前类加载器的线程  参考文档

问题2:同步方法和同步代码块,哪个是更好的选择?

  解析:

  同步方法:在方法上加入关键字:synchronized的方法

  同步代码块:在代码块上加入关键字:synchronized(Object){}  (小括号内即为加锁的对象)

  选用同步代码块好,因为可以只将可能出现并发问题的代码放入同步代码块中,且同步代码块可以自行选择加锁的对象。同步方法的锁对象是当前类的实例

问题3:如何检测死锁,怎么预防死锁?

  解析:

  死锁:两个或者两个以上的线程在执行过程中,因为争夺资源而造成的互相等待的现象。(锁已经没有人拿了,但是现在所有的线程都在等待拿锁,而不是去拿锁)

  死锁的四个必要条件:

  互斥条件:资源只能被一个线程占用,若资源已经被占用,其他线程需要等待知道占用线程释放资源。

  请求并持有条件:一个线程已经占用至少一个资源,又提出新的资源占用,但是新资源被其他线程占用,所以当前线程被阻塞,且不会释放已经占用的资源。

  不可剥夺条件:线程占用资源后,在使用完之前是不会释放的,且不可能被其他线程夺走。

  环路等待条件:发生死锁时,必然存在一个线程——资源的环形链。(请求并持有条件串成环既是)。

  避免死锁需要破坏至少一个死锁的必要条件即可,实际上只有请求并持有和环路等待是可以破坏的。

  那么如何破坏请求并持有条件和环路条件呢?实际上通过资源申请的有序性就可以实现。(反过来理解也可以认为是因为资源申请的有序性无法得到保障才导致的请求并持有和环路等待)


15日面试题

问题1:线程池作用,主要实现类,并说出实现类场景以及区别。

解析:

  线程池的作用跟解决的问题其实是一个概念:

  解决两个问题:

    一:大量异步任务时,线程池能够提供更好的性能:线程可重复利用,避免创建线程和销毁线程带来的资源损耗。

    二:线程池提供了一种资源限制和管理的手段,可以限制线程的个数,也可动态新增线程池。

  线程池以及场景和区别:

    1.newFixedThreadPool:创建了一个核心线程个数和最大线程个数都为nThreads的线程池,且其阻塞队列长度为Integer.MAX_VAlUE。其keepAliveTime=0说明只要线程个数比核心线程个数多并且当前空闲就回收。注:关于这个keepAliveTime=0的回收概念,我个人暂时有点不理解(因为核心线程数和最大线程数是一个值)。

    2.newSingleThreadExecutor:创建一个核心线程个数和最大线程个数都为1的线程池,且阻塞队列长度为Integer.Max_VALUE。keepAliveTime=0说明线程个数比核心线程个数多且当前空闲就回收。

    3.newCachedThreadPool:创建一个按需创建线程的线程池,初始线程个数为0,最多线程个数为Integer.MAX_VALUE,并且阻塞队列为同步队列,keepAliveTime=60说明只要当前线程在60s内空闲则回收。这个类型特殊在加入同步队列的任务会被马上执行,同步队列中最多只有一个任务。

    4.ScheduledThreadPoolExecutor:主要是指定一定延迟时间后或者定时执行任务调度执行的线程池。

问题2:ThreadPoolExecutor使用场景,以及原理。

解析

  实现原理:当向线程池提交一个任务后,线程池进行处理:

    一:线程池判断线程池的线程是否都在执行任务,如果不是,则创建一个新的工作线程来执行任务(使用threadFactory创建),如果线程都在执行任务,进入下个流程。

    二:线程池判断工作队列是否满了,如果没有,则将新提交的任务存储在工作队列中(对应构造函数中的workQueue),如果满了进入下个流程。

    三:线程池判断线程池是否已经满了(线程已经达到最大数,且任务队列已经满了),如果没有,则创建一个新的工作线程执行任务,如果满了,任务将被拒绝并交给饱和策略来处理这个任务。

  应用场景其实就是问题一种的前三个线程池的应用场景:

    一:FixedThreadPool:保证所有任务都执行,永远不会拒绝新任务;缺点是:队列数量没有限制,在任务执行无限延长的极端情况下可能造成内存问题。

    二:SingleThreadExecutor:适用于逻辑上需要单线程的场景,同时无解的LinkedBlockingQueue保证新任务都能够放入队列,不会被拒绝,缺点也是可能会造成内存问题。

    三:CachedThreadPool:提交的任务会立即分配线程执行,线程的数量会随着任务数进行扩展和缩减,问题是可能会创建过多的线程。

问题3:Executor拒绝策略说的是什么?

解析

  一共四中拒绝策略:

    一:ThreadPoolExecutor.AbortPolicy  抛弃任务并抛出RejectedExecutionException

    二:ThreadPoolExecutor.DiscardPolicy  抛弃任务,但是不抛出异常

    三:ThreadPoolExecutor.DiscardOldestPlicy  抛弃队列最前面的任务,执行后面的任务

    四:ThreadPoolExecutor.callerRunsPolicy   由调用线程处理该任务

问题4:无界阻塞延迟队列的delayqueue原理是什么?

解析

  是一个支持延期获取元素的无界限队列,队列使用PriorityQueue实现,队列中的元素必须实现Delayed接口,创建元素时可以指定多久才能从队列中获取当前元素,只有在延时期满时才能从队列中获取元素。

  PriorityQueue是一个优先级的队列,队列中的元素会按照优先级进行排序。

详解

问题5:CyclicBarrier和CountDownLatch的区别?

解析

  CountDownLatch:可以实现计数器功能,使线程按照一定执行顺序执行。

  CyclicBarrier:可以让一组线程等待至某个状态之后再全部执行,当所有等待线程被释放后,CyclicBarrier能够重复使用

详解

15日反思:关于并发方面差的东西有点多,最近正在补,目前看的书是《Java并发编程之美》,希望能尽快吸收消化进步。

争取能够回头再把问题答案重新整理一遍


16日面试题

问题1:Java中的同步集合与并发集合有什么区别

解析:

  同步集合:

    - Hashtable

    - Vector

    - 同步集合包装类:Collections下面的方法可以获取线程安装的同步集合:

      synchronizedCollection(Collection<T> c)

      synchronizedList(List<T> list)

      synchronizedMap(Map<k, v> m)

      synchronizedSet(Set<T> set)

      synchronizedSortedMap(SortedMap<k ,v> sm)

    同步集合的优缺点:在单线程环境下可以保证数据安全,但是是通过synchronized关键字实现同步方法将访问操作串行化,导致在并发环境中效率降低。且在多线程环境下的复合操作是非线程安全的,需要加锁来实现。

  并发集合:

    Java提供了两类的并发集合:

      - 阻塞式集合(blocked collection):这类集合包含添加和移除的方法,当集合已满或者为空时被调用的添加或者移除方法就不能立刻执行,那么调用这个方法的线程将会被阻塞,一直到改方法被调用成功。

      - 非阻塞式集合(Non-blocked collection): 这类集合包含添加和移除方法,如果集合已满或者为空时,调用添加或移除方法会返回null或者报错,但是调用这个方法的线程不会被阻塞。

    常见的并发集合包括:

      - 非阻塞列表对应的实现类:ConcurrentLinkedDeque

      - 阻塞式列表对于的实现类:LinkedBlockingDeque

      - 用于数据生成或者消费的阻塞式列表对应的实现类:LinkedTransferQueue

      - 按照优先级排序列表元素的阻塞式列表对应的实现类:PriorityBlockingQueue

      - 带有延迟列表元素的阻塞式列表对应的实现类:DelayQueue

      - 非阻塞式列表可遍历映射对应的实现类ConcurrentSkipListMap

      - 随机数字对应的实现类:ThreadLockRandom

      - 原子变量对应的实现类:AtomicLong和AtomicIntegerArray

问题2:java中invokeAndWait和invokeLater有什么区别?

解析:

  事件派发线程是swing的组件,swing是事件驱动的,所有的操作都需要事件派发线程去完成,但是有些很费事的操作不太好放到事件派发线程中去。

  SwingUtils提供了两个方法:invokeAndWait和invokeLater,他们都是事件派发线程可运行的对象,当对象位于事件派发队列的队首时,他们就被执行其中的run(),方法是允许事件派发线程调用另一个线程中的任意一个方法。

  invokeAndWait和invokeLater都可将可运行对象放到事件派发队列中去,但是invokeLater将对象放入队列就返回了,invokeAndWait将对象放入后直到已启动了可运行的run()时才返回。

问题3:什么是FutureTask

解析:

  Runnable是一个接口,封装了一个没有参数和返回值异步执行的方法。

  Future也是一个接口,且其中保存异步计算的结果:

代码语言:javascript
复制
public interface Future<V> {
    boolean cancel(boolean mayInterruptIfRunning);
    boolean isCancelled();
    boolean isDone();
    V get() throws InterruptedException, ExecutionException;
    V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}

  FutureTask则同时实现了上述两个接口

FutureTask内部提供了定义了以下变量:

  volatile int state:表示对象状态,volatile关键字保证了内存可见性。futureTask中定义了7种状态,代表了7种不同的执行状态

代码语言:javascript
复制
private static final int NEW          = 0; //任务新建和执行中
private static final int COMPLETING   = 1; //任务将要执行完毕
private static final int NORMAL       = 2; //任务正常执行结束
private static final int EXCEPTIONAL  = 3; //任务异常
private static final int CANCELLED    = 4; //任务取消
private static final int INTERRUPTING = 5; //任务线程即将被中断
private static final int INTERRUPTED  = 6; //任务线程已中断

  Callable callable:被提交的任务

  Object outcome:任务执行结果或者任务异常

  volatile Thread runner:执行任务的线程

  volatile WaitNode waiters:等待节点,关联等待线程

  long stateOffset:state字段的内存偏移量

  long runnerOffset:runner字段的内存偏移量

  long waitersOffset:waiters字段的内存偏移量   注:后三个字段是配合Unsafe类做CAS操作使用的。

代码语言:javascript
复制
创建一个futureTask对象task
提交task到调度器executor等待调度或者在另外一个线程中执行task

等待调度中...

如果此时currentThread调取执行结果task.get(),会有几种情况
if task 还没有被executor调度或正在执行中
    阻塞当前线程,并加入到一个阻塞链表中waitNode
else if task被其它Thread取消,并取消成功 或task处于中断状态
    throw exception
else if task执行完毕,返回执行结果,或执行存在异常,返回异常信息
    
        
如果此时有另外一个线程调用task.get()
    
执行过程同上

总结下,FutureTask的状态流转过程,可以出现以下四种情况: 1. 任务正常执行并返回。 NEW -> COMPLETING -> NORMAL 2. 执行中出现异常。NEW -> COMPLETING -> EXCEPTIONAL 3. 任务执行过程中被取消,并且不响应中断。NEW -> CANCELLED 4.任务执行过程中被取消,并且响应中断。 NEW -> INTERRUPTING -> INTERRUPTED

参考链接 参考链接


本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-03-14 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 14日面试题:
    • 问题1:线程类的构造方法,静态代码块是被哪个线程调用的?
      • 解析:
    • 问题2:同步方法和同步代码块,哪个是更好的选择?
      •   解析:
    • 问题3:如何检测死锁,怎么预防死锁?
      •   解析:
  • 15日面试题
    • 问题1:线程池作用,主要实现类,并说出实现类场景以及区别。
      • 解析:
    • 问题2:ThreadPoolExecutor使用场景,以及原理。
      • 解析
    • 问题3:Executor拒绝策略说的是什么?
      • 解析
    • 问题4:无界阻塞延迟队列的delayqueue原理是什么?
      • 解析
    • 问题5:CyclicBarrier和CountDownLatch的区别?
      • 解析
  • 16日面试题
    • 问题1:Java中的同步集合与并发集合有什么区别
      • 解析:
    • 问题2:java中invokeAndWait和invokeLater有什么区别?
      • 解析:
    • 问题3:什么是FutureTask
      • 解析:
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档