JAVA多线程高并发面试题总结

ReadMe : 括号里的内容为补充或解释说明。html

多线程和高并发是毕业后求职大厂面试中必问的知识点,本身以前老是面试前才去找相关的知识点面试题来背背,隔段时间又忘了,没有沉淀下来,因而本身总结了下相关的知识点。面试

多线程

1. 进程和线程之间有什么不一样?

  进程是一个独立的运行环境,它能够被看做是一个程序或者一个应用。而线程是在进程中执行的一个任务。进程是操做系统进行资源分配的基本单位,而线程是操做系统进行调度的基本单位。进程让操做系统的并发性成为可能,而线程让进程的内部并发成为可能。比如Java运行环境是一个(包含了不一样的类和程序的)单一进程。缓存

想理解的更深入,请点击进程与线程的区别.安全

2. Thread 类中的start() 和run() 方法有什么区别?

    1) start()被用来启动新的线程,run()不能。多线程

  2)start()不能被重复调用,run()能够。并发

  3)start()中的run代码能够不执行完就继续执行下面的代码,即线程转换,若是直接调用run()必须等待其代码所有执行完才能继续执行下面的代码。异步

  4)start()实现了多线程,run()没有实现多线程。ide

3.  在多线程中,什么是上下文切换?

  上下文切换是存储和恢复CPU状态的过程,它使得线程执行可以从中断点恢复执行。是多任务操做系统和多线程环境的基本特征。函数

4. Java中的volatile 变量是什么?

   volatile是一个特殊的修饰符,只有成员变量(类的成员变量、类的静态成员变量)才能使用它。高并发

被volatile修饰以后就具有了两层语义:

  1)保证了不一样线程对这个变量进行操做时的可见性(即一个线程修改了某个变量的值,这新值对其余线程来讲是即刻可见的)。

  2)禁止进行指令重排序。

5. Java中堆和栈有什么不一样?(相对于线程来讲)

  栈是一块和线程紧密相关的内存区域。每一个线程都有本身的栈内存,用于存储本地变量,方法参数和栈调用,一个线程中存储的变量对其它线程是不可见的。

  堆是全部线程共享的一片公用内存区域。对象都在堆里建立,为了提高效率线程会从堆中弄一个缓存到本身的栈,若是多个线程使用该变量就可能引起问题,这时volatile 变量就能够发挥做用了,它要求线程从主存中读取变量的值。

6.  什么是线程池? 为何要使用它?

  建立线程要花费资源和时间,若是任务来了才建立线程那么响应时间会变长,并且一个进程能建立的线程数有限。为了不这些问题,在程序启动的时候就建立若干线程来响应处理,它们被称为线程池,里面的线程叫工做线程。

7. 死锁是什么?如何避免死锁?

  死锁是指两个或两个以上的进程在执行过程当中,因争夺资源而形成的一种互相等待的现象,若无外力做用,它们都将没法推动下去。这是一个严重的问题,由于死锁会让你的程序挂起没法完成任务,死锁的发生必须知足如下四个条件:

1)互斥条件:一个资源每次只能被一个进程使用。

2)请求与保持条件:一个进程因请求资源而阻塞时,对已得到的资源保持不放。

3)不剥夺条件:进程已得到的资源,在末使用完以前,不能强行剥夺。

4)循环等待条件:若干进程之间造成一种头尾相接的循环等待资源关系。

  避免死锁最简单的方法就是阻止循环等待条件,将系统中全部的资源设置标志位、排序,规定全部的进程申请资源必须以必定的顺序(升序或降序)作操做来避免死锁。

8. Thread类中的yield方法有什么做用?

  Thread.yield() 方法会使当前线程从运行状态变为就绪状态,把运行机会让给其它相同优先级的线程。它是一个静态的原生(native)方法并且只保证当前线程放弃CPU占用而不能保证使其它线程必定能占用CPU,执行yield()的线程有可能会被再次继续执行的。

9. Java中notify 和 notifyAll有什么区别?

  调用notify时,只有一个等待线程会被唤醒并且它不能保证哪一个线程会被唤醒,这取决于线程调度器。虽然若是你调用notifyAll方法,那么等待该锁的全部线程都会被唤醒。

10.  Java中interrupted 和 isInterruptedd方法的区别?

  interrupted() 和 isInterrupted()的主要区别是前者会将中断状态清除然后者不会。

  Java多线程的中断机制是用内部标识来实现的,调用Thread.interrupt()来中断一个线程就会设置中断标识为true。当中断线程调用静态方法Thread.interrupted()来检查中断状态时,中断状态会被清零。而非静态方法isInterrupted()用来查询其它线程的中断状态且不会改变中断状态标识。简单的说就是任何抛出InterruptedException异常的方法都会将中断状态清零。不管如何,一个线程的中断状态有有可能被其它线程调用中断来改变。

11. Java多线程中调用wait() 和 sleep()方法有什么不一样?

  sleep()和wait()都是使线程暂停执行一段时间的方法。两者区别为:

1)原理不一样。

  sleep()方法是Thread类的静态方法,是线程用来控制自身流程的,它会使此线程暂停执行一段时间,而把执行机会让给其余线程,等到计时时间一到,此线程会自动苏醒。而wait()方法是Object类的方法,用于线程间的通讯,这个方法会使当前拥有该对象锁的进程等待,直到其余线程用调用notify()或notifyAll()时才苏醒过来,开发人员也能够给它指定一个时间使其自动醒来。

2)对锁的处理机制不一样。

  因为sleep()方法的主要做用是让线程暂停一段时间,时间一到则自动恢复,不涉及线程间的通讯,所以调用sleep()方法仅仅释放CPU资源或者让当前线程中止执行一段时间,但不会释放锁。而wait()方法则不一样,当调用wait()方法后,线程会释放掉它所占用的锁,从而使线程所在对象中的其余synchronized数据可被别的线程使用。

3)使用区域不一样。

  wait()方法必须放在同步控制方法或者同步语句块中使用,而sleep方法则能够放在任何地方使用。sleep()方法必须捕获异常,而wait()、notify()、notifyAll()不须要捕获异常。在sleep的过程当中,有可能被其余对象调用它的interrupt(),产生InterruptedException异常。

  因为sleep不会释放锁标志,容易致使死锁问题的发生,通常状况下,不推荐使用sleep()方法,而推荐使用wait()方法。

 12. 有三个线程T1,T2,T3,怎么确保它们按顺序执行?

在多线程中有多种方法让线程按特定顺序执行,你能够用线程类的join()方法在一个线程中启动另外一个线程,另一个线程完成该线程继续执行。为了确保三个线程的顺序你应该先启动最后一个(T3调用T2,T2调用T1),这样T1就会先完成而T3最后完成。

13.  如何建立守护线程?

  使用Thread类的setDaemon(true)方法能够将线程设置为守护线程,须要注意的是,须要在调用start()方法前调用这个方法,不然会抛出IllegalThreadStateException异常。

 
/**
 * @author liao.wenhui
 * @date 2019/7/15 15:13
 */
public class DaemonThread {
    public static void main(String[] args) {
        Thread daemonThread = new Thread(new Runnable() {
            @Override
            public void run() {

            }
        });

        //设置守护线程
        daemonThread.setDaemon(true);
        daemonThread.start();
    }
}
 

前提知识:

  守护进程(Daemon)是运行在后台的一种特殊进程。它独立于控制终端而且周期性地执行某种任务或等待处理某些发生的事件(百度百科)。

  Java线程分为两类分别为daemon线程(守护线程)和User线程(用户线程),在JVM启动时候会调用main函数,main函数所在的线程是一个用户线程,这个是咱们能够看到的线程,其实JVM内部同时还启动了好多守护线程,好比垃圾回收线程。那么守护线程和用户线程有什么区别那?区别之一是当最后一个非守护线程结束时候,JVM会正常退出,而无论当前是否有守护线程,也就是说守护线程是否结束并不影响JVM的退出。言外之意是只要有一个用户线程还没结束正常状况下JVM就不会退出。

14. 什么是线程调度器(Thread Scheduler)和时间分片(Time Slicing)?

  线程调度器是一个操做系统服务,它负责为Runnable状态的线程分配CPU时间。一旦咱们建立一个线程并启动它,它的执行便依赖于线程调度器的实现。

  时间分片是指将可用的CPU时间分配给可用的Runnable线程的过程。分配CPU时间能够基于线程优先级或者线程等待的时间。线程调度并不受到Java虚拟机控制,因此由应用程序来控制它是更好的选择(即最好不要让你的程序依赖于线程的优先级)。

15.什么是ThreadLocal?

  ThreadLocal用于建立线程的本地变量,咱们知道一个对象的全部线程会共享它的全局变量,因此这些变量不是线程安全的,咱们可使用同步技术。可是当咱们不想使用同步的时候,咱们能够选择ThreadLocal变量。每一个线程都会拥有他们本身的Thread变量,它们可使用get()/set()方法去获取他们的默认值或者在线程内部改变他们的值。

16.Java线程池中submit() 和 execute()方法有什么区别?

  两个方法均可以向线程池提交任务,execute()方法的返回类型是void,它定义在Executor接口中, 而submit()方法能够返回持有计算结果的Future对象,它定义在ExecutorService接口中,它扩展了Executor接口,其它线程池类像ThreadPoolExecutor和ScheduledThreadPoolExecutor都有这些方法。

17.  Java中Runnable和Callable有什么不一样?

  Runnable和Callable都表明那些要在不一样的线程中执行的任务。Runnable从JDK1.0开始就有了,Callable是在JDK1.5增长的。它们的主要区别是Callable的 call() 方法能够返回任务执行结果值和抛出异常,而Runnable的run()方法没有这些功能。Callable能够返回装载有计算结果的Future对象。

高并发

1. 什么是FutureTask?

  在Java并发程序中FutureTask表示一个能够取消的异步运算。它有启动和取消运算、查询运算是否完成和取回运算结果等方法。只有当运算完成的时候结果才能取回,若是运算还没有完成get方法将会阻塞。一个FutureTask对象能够对调用了Callable和Runnable的对象进行包装,因为FutureTask也是调用了Runnable接口因此它能够提交给Executor来执行。

相关文章
相关标签/搜索