多线程相关面试题及其解答

作者: TinyDolphin | 来源:发表于2017-12-17 23:48 被阅读326次

    Q:什么是线程?什么是进程?线程和进程有什么区别?

    A:线程:又称"轻量级进程",是操作系统能够进行运算调度的最小单位,被包含在进程之中,是进程中的实际运作单位。

    进程: 系统进行资源分配和调度的基本单位。

    两者之间的区别:
    地址空间和其他资源:进程间相互独立,同一进程的各线程间共享,某进程中的线程在其他进程不可见。
    通信:进程间通信 IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。
    调度和切换:线程上下文切换比进程上下文切换要快得多。
    在多线程 OS 中,进程不是一个可执行的实体。


    Q:多线程编程的好处是什么?

    A:发挥多核 CPU 的优势(提高性能)、防止阻塞、便于建模


    Q:有几种创建线程的方式?哪种比较好?

    A:①、实现 Runnable 接口(较好:灵活,减少耦合度);②、继承 Thread 类。


    Q:Runnable 接口和 Callable 接口的区别?

    A:Callable 接口中的 call() 方法是可以返回值和抛出异常,是一个泛型,和 Future / FutureTask 配合来获取结果。而 Runnalbe 接口的 run() 方法,无返回值。


    Q:Java 中 CyclicBarrier 和 CountDownLatch 有什么不同?

    A:①、CyclicBarrier 是 n 个线程,相互等待;CountDownLatch 是一个或多个线程,等待另外 n 个线程执行完,再执行。
    ②、CyclicBarrier 计数;CountDownLatch 计数。
    ③、CyclicBarrier 计数达到指定值时,计数置为 0 重新开始;CountDownLatch 计数为 0 时,无法重置。(不可重复利用
    ④、CyclicBarrier 调用 await() 方法计数加 1 , 若加 1 后的值不等于构造方法的值,则线程阻塞;CountDownLatch 调用 countDown() 方法计数减 1 ,调用 await() 方法只进行阻塞,对计数没任何影响。


    Q:volatile 是什么?作用是什么?

    A:volatile 是一个类型修饰符,是用来修饰被不同线程访问和修改的变量

    主要作用有两个:①、内存可见性,即线程 A 对 volatile 变量的修改,其他线程获取的 volatile 变量都是最新的;②、可以禁止指令(程序执行的顺序)重排序


    Q:什么是线程安全?

    A:线程安全:多线程访问时,采用了加锁机制,当一个数据访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。
    线程不安全不提供数据访问保护,有可能多个线程先后更改数据造成所得到的数据是脏数据


    Q:线程安全分为哪几种级别?

    A:①、不可变不变的对象绝对是线程安全的,不需要线程同步,如 String、Long、BigInteger;
    ②、无条件的线程安全:对象自身做了足够多的内部同步,也不需要外部同步,如 Random、ConcurrentHahsMap、Concurrent 集合、atomic;
    ③、有条件的线程安全:对象的部分方法可以无条件安全使用,但是有些地方需要外部同步,需要 Collectiongs.synchronized;有条件线程安全的最常见的例子是遍历由 HashTable 或者 Vector 或者返回的迭代器;
    ④、非线程安全(线程兼容):对象本身不提供线程安全机制,但是通过外部同步,可以在并发环境使用,如 ArrayList、HashMap;
    ⑤、线程对立:即使外部进行了同步调用,也不能保证线程安全,这种情况非常少,如 System.setOut()、System.runFinalizersOnExit()。


    Q:如何正确地停止一个线程?

    A:①、使用退出标志,使线程正常退出,也就是当 run 方法完成后线程终止;
    ②、使用 interrupt 方法中断线程
    ③、使用 stop 方法强行终止,但是不推荐使用,因为 stop 和 suspend 以及 resume 一样存在死锁威胁,并且都是过期作废的方法。


    Q:一个线程运行时发生异常会怎样?

    A:①、如果异常没有被捕获,该线程将会停止执行
    ②、如果这个线程持有某个对象的监视器,那么这个对象监视器会被立即释放


    Q:两个线程之间,如何实现数据共享?

    A:①、使用同一个 Runnable 对象;
    ②、使用不同的 Runnable 对象:
    i、将共享数据分别传递给两个不同的线程
    ii、将这些 Runnable 对象作为一个内部类,将共享数据作为成员变量


    Q:sleep() 方法和 wait() 方法有什么区别?

    A:①、所在的类:sleep 在 Thread 类中、wait 在 Object 类中;
    ②、锁:sleep 方法没有释放锁、wait 方法释放了锁
    ③、使用范围:sleep 可以在任何地方使用、而 wait,notify 和 notifyAll 只能在同步控制方法或者同步控制块里面使用


    Q:ThreadLoacl 是什么?作用是什么?

    A:它是线程局部变量,每个线程都有自己的 ThreadLocal。

    作用:把数据进行隔离,数据不共享(空间换时间,在 Thread 中维护了一个以开地址法实现的 ThreadLocalMap )。


    Q:为什么 wait() 方法和 notify()/notifyAll() 方法要在同步块( synchronized )中被调用?

    A:JDK 强制的,方法调用之前必须先获得对象的锁。如果不这样做,就会抛出 IllegalMonitorStateException 异常。


    Q:同步集合 & 并发集合的区别?

    A:都支持线程安全,主要区别体现在性能可扩展性

    性能:同步集合会把整个 Map 或者 List 锁起来,而并发集合不会。所以同步集合比并发集合慢的多。
    可扩展性并发集合不仅提供线程安全,还用锁分离和内部分区等现代技术来提高可扩展性。


    Q:什么是线程池? 为什么要使用它?

    A:在程序启动时,就创建若干个线程来响应请求。使用它,可以避免频繁地创建和销毁线程,达到线程对象的重用,还可以根据项目灵活地控制并发的数目


    Q:什么是死锁?死锁发生的必要条件?如何避免死锁?

    A:死锁:两个及以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,他们都将无法推进下去。

    死锁的必要条件
    ①、互斥条件:一个资源每次只能被一个进程使用;
    ②、请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不变;
    ③、不剥夺条件:进程已获得的资源,在未使用之前,不能强行剥夺;
    ④、循环等待条件:若干进程之间形成一种头尾相连的循环等待资源关系。

    避免死锁最简单的方法阻止循环等待条件,将系统中所有的资源设置标志位、排序,规定所有的进程申请资源必须以一定的顺序做操作。


    Q:活锁 & 死锁的区别?

    A:活锁进程的状态可以改变但是却不能继续执行


    Q:怎么检测一个线程是否拥有锁?

    A:Thread 中的 holdsLock() 方法:如果当且仅当当前线程拥有某个具体对象的锁时,返回 true


    Q:synchronized & ReentrantLock 的区别?

    A:两者都是加锁方式同步(阻塞式同步)

    其本质上的区别:synchronized 是关键字,ReentrantLock 是类。
    相对于 synchronized 而言,ReentrantLock 类提供了一些高级功能:
    ①、等待可中断:持有锁的线程长期不释放时,正在等待的线程可以选择放弃等待,这相等于 synchronized 来说,避免了死锁
    ②、公平锁:synchronized 只能是非公平锁,ReenTrantLock 可以通过构造函数的参数来指定公平锁和非公平锁。(公平锁:先等待的线程先得到锁);
    ③、锁绑定多个条件:一个 ReentrantLock 对象可以同时绑定多个对象。
    ④、锁机制不一样:synchronized


    Q:如何保证线程按照指定顺序执行?

    A:使用 new Thread().join() 方法:等待线程结束。也就是说通过一个线程等待另一个线程执行完毕,再继续执行,来实现。


    Q:如果你提交任务时,线程池队列已满。会时发会生什么?

    A:当一个任务不能被调度执行时,ThreadPoolExecutor 的 submit() 方法将会抛出 RejectedExecutionException 异常。


    Q:Java 线程池中 submit() & execute() 方法有什么区别?

    A:①、接收的参数不一样;②、submit() 有返回值,execute() 没有
    ;③、submit() 方便 Exception 处理


    Q:ReadWriteLock 是什么?

    A:维护一对关联的锁,一个用于只读操作,一个用于写操作读锁是共享的,而写锁是独占的。


    Q:多线程中的忙循环是什么?使用它的目的是什么?

    A:忙循环:用循环让一个线程等待,不会放弃 CPU

    目的:为了保留 CPU 缓存,避免重建缓存和减少等待重建时间。在多核系统中,一个等待线程醒来时,可能会在另一个内核运行,这样就会重建缓存。


    Q:遵循的多线程的最佳实践有哪几条?

    A:①、给线程起一个有意义的名字;②、避免锁定和缩小同步的范围;③、多用同步类,少用 wait 和 notify;④、多用并发集合,少用同步集合。


    Q:如何强制启动一个线程?

    A:它是被线程调度器控制的,并且 Java 没有公布相关的 API。


    Q:fork-join 框架是什么?

    A:Java7 提供了的一个用于并行执行任务的框架, 是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架。使用了工作窃取算法

    相关文章

      网友评论

      • 喜欢敲代码的猫:什么是线程退出标志啊
        TinyDolphin:当 run 方法执行完后线程终止,比如定义一个变量,当这个变量满足一定的条件时,结束 run 方法即可使线程终止。

      本文标题:多线程相关面试题及其解答

      本文链接:https://www.haomeiwen.com/subject/prmxbxtx.html