多线程面试题总结

线程

线程
1、创建线程的方式及实现
答:A、继承Thread类创建线程。B、实现Runnable接口创建线程 C、使用Callable和Future创建线程
------------------------继承Thread类创建线程---------------------
通过继承Thread类来创建并启动多线程的一般步骤如下:
1】d定义Thread类的子类,并重写该类的run()方法,该方法的方法体就是线程需要完成的任务,run()方法也称为线程执行体。
2】创建Thread子类的实例,也就是创建了线程对象
3】启动线程,即调用线程的start()方法
代码实例
public class MyThread extends Thread{//继承Thread类
  public void run(){
   //重写run方法
  }
}
public class Main {
  public static void main(String[] args){
    new MyThread().start();//创建并启动线程
  }
}
------------------------实现Runnable接口创建线程---------------------
通过实现Runnable接口创建并启动线程一般步骤如下:
1】定义Runnable接口的实现类,一样要重写run()方法,这个run()方法和Thread中的run()方法一样是线程的执行体
2】创建Runnable实现类的实例,并用这个实例作为Thread的target来创建Thread对象,这个Thread对象才是真正的线程对象
3】第三部依然是通过调用线程对象的start()方法来启动线程
代码实例:
public class MyThread2 implements Runnable {//实现Runnable接口
  public void run(){
   //重写run方法
  }
}
public class Main {
  public static void main(String[] args){
    //创建并启动线程
    MyThread2 myThread=new MyThread2();
    Thread thread=new Thread(myThread);
    thread().start();
    //或者 new Thread(new MyThread2()).start();
  }
}
------------------------使用Callable和Future创建线程---------------------
和Runnable接口不一样,Callable接口提供了一个call()方法作为线程执行体,call()方法比run()方法功能要强大。
》call()方法可以有返回值
》call()方法可以声明抛出异常
Java5提供了Future接口来代表Callable接口里call()方法的返回值,并且为Future接口提供了一个实现类FutureTask,这个实现类既实现了Future接口,还实现了Runnable接口,因此可以作为Thread类的target。在Future接口里定义了几个公共方法来控制它关联的Callable任务。
>boolean cancel(boolean mayInterruptIfRunning):视图取消该Future里面关联的Callable任务
>V get():返回Callable里call()方法的返回值,调用这个方法会导致程序阻塞,必须等到子线程结束后才会得到返回值
>V get(long timeout,TimeUnit unit):返回Callable里call()方法的返回值,最多阻塞timeout时间,经过指定时间没有返回抛出TimeoutException
>boolean isDone():若Callable任务完成,返回True
>boolean isCancelled():如果在Callable任务正常完成前被取消,返回True

介绍了相关的概念之后,创建并启动有返回值的线程的步骤如下:
1】创建Callable接口的实现类,并实现call()方法,然后创建该实现类的实例(从java8开始可以直接使用Lambda表达式创建Callable对象)。
2】使用FutureTask类来包装Callable对象,该FutureTask对象封装了Callable对象的call()方法的返回值
3】使用FutureTask对象作为Thread对象的target创建并启动线程(因为FutureTask实现了Runnable接口)
4】调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
代码实例:
public class Main {
  public static void main(String[] args){
   MyThread3 th=new MyThread3();
   //使用Lambda表达式创建Callable对象
     //使用FutureTask类来包装Callable对象
   FutureTask<Integer> future=new FutureTask<Integer>(
    (Callable<Integer>)()->{
      return 5;
    }
    );
   new Thread(task,"有返回值的线程").start();//实质上还是以Callable对象来创建并启动线程
    try{
    System.out.println("子线程的返回值:"+future.get());//get()方法会阻塞,直到子线程执行结束才返回
    }catch(Exception e){
    ex.printStackTrace();
   }
  }

}

2、sleep() 、join()、yield()有什么区别
答:sleep()方法需要指定等待的时间,它可以让当前正在执行的线程在指定的时间内暂停执行,进入阻塞状态,该方法既可以让其他同优先级或者高优先级的线程得到执行的机会,也可以让低优先级的线程得到执行机会。但是sleep()方法不会释放“锁标志”,也就是说如果有synchronized同步块,其他线程仍然不能访问共享数据。   
wait() :
  wait()方法需要和notify()及notifyAll()两个方法一起介绍,这三个方法用于协调多个线程对共享数据的存取,所以必须在synchronized语句块内使用,也就是说,调用wait(),notify()和notifyAll()的任务在调用这些方法前必须拥有对象的锁。注意,它们都是Object类的方法,而不是Thread类的方法。 
  wait()方法与sleep()方法的不同之处在于,wait()方法会释放对象的“锁标志”。当调用某一对象的wait()方法后,会使当前线程暂停执行,并将当前线程放入对象等待池中,直到调用了notify()方法后,将从对象等待池中移出任意一个线程并放入锁标志等待池中,只有锁标志等待池中的线程可以获取锁标志,它们随时准备争夺锁的拥有权。当调用了某个对象的notifyAll()方法,会将对象等待池中的所有线程都移动到该对象的锁标志等待池。 
  除了使用notify()和notifyAll()方法,还可以使用带毫秒参数的wait(long timeout)方法,效果是在延迟timeout毫秒后,被暂停的线程将被恢复到锁标志等待池。 
  此外,wait(),notify()及notifyAll()只能在synchronized语句中使用,但是如果使用的是ReenTrantLock实现同步,该如何达到这三个方法的效果呢?解决方法是使用ReenTrantLock.newCondition()获取一个Condition类对象,然后Condition的await(),signal()以及signalAll()分别对应上面的三个方法。
yield() :
  yield()方法和sleep()方法类似,也不会释放“锁标志”,区别在于,它没有参数,即yield()方法只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行,另外yield()方法只能使同优先级或者高优先级的线程得到执行机会,这也和sleep()方法不同。
join() :

  join()方法,让调用线程在当前线程对象上进行等待。当线程执行完成后,被等待的线程会在退出前调用notifyAll()通知所有的等待线程继续执行。

3、说说 CountDownLatch 原理
    CountDownLatch也叫闭锁,是计数器自减的一种闭锁,某线程阻塞,对一个计数器自减到0,此线程被唤醒。在JDK1.5被引入,允许一个或多个线程等待其他线程完成操作后再执行。
    CountDownLatch内部会维护一个初始值为线程数量的计数器,主线程执行await方法,如果计数器大于0,则阻塞等待。当一个线程完成任务后,计数器值减1。当计数器为0时,表示所有的线程已经完成任务,等待的主线程被唤醒继续执行。
    CountDownLatch是一种共享锁,通过await()方法与countDown()两个方法实现自身的功能。
    await() 实现是由主线程执行await方法,tryAcquireShared方法中如果state不等于0,返回-1,则加入到等待队列中,主线程通过LockSupport.park(this)被挂起。

    countdown() 委托sync实现state的减1操作,即通过unsafe.compareAndSwapInt方法设置state值。如果state为0,通 过LockSupport.unpark唤醒await方法中挂起的主线程。

4、说说 CyclicBarrier 原理   参考

答:https://www.cnblogs.com/nullzx/p/5271964.html

5、说说 Semaphore 原理   参考

答:https://blog.csdn.net/coslay/article/details/45176063

6、说说 Exchanger 原理  参考

答:https://blog.csdn.net/chenssy/article/details/72550933

7、说说 CountDownLatch 与 CyclicBarrier 区别
答: 1、CountDownLatch简单的说就是一个线程等待,直到他所等待的其他线程都执行完成并且调用countDown()方法发出通知后,当前线程才可以继续执行。
2、cyclicBarrier是所有线程都进行等待,直到所有线程都准备好进入await()方法之后,所有线程同时开始执行!
3、CountDownLatch的计数器只能使用一次。而CyclicBarrier的计数器可以使用reset() 方法重置。所以CyclicBarrier能处理更为复杂的业务场景,比如如果计算发生错误,可以重置计数器,并让线程们重新执行一次。

4、CyclicBarrier还提供其他有用的方法,比如getNumberWaiting方法可以获得CyclicBarrier阻塞的线程数量。isBroken方法用来知道阻塞的线程是否被中断。如果被中断返回true,否则返回false。

8、Java中用到的线程调度算法是什么    参考

答: https://www.jianshu.com/p/3f6b26ee51ce

9、什么是多线程中的上下文切换
答:
10、你对线程优先级的理解是什么

11、什么是线程调度器 (Thread Scheduler) 和时间分片 (Time Slicing)

12、ThreadLocal 原理分析
答   https://www.cnblogs.com/dolphin0520/p/3920407.html
13、ThreadPool用法与优势

14、讲讲线程池的实现原理 参考
答  https://blog.csdn.net/mine_song/article/details/70948223
15、线程池的几种方式与使用场景

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

17、线程池中的coreNum和maxNum有什么不同

18、在不同的业务场景中,线程池参数如何设置?

19、线程池的关闭方式有几种,各自的区别是什么

20、newCache 和 newFixed 有什么区别?简述原理。构造函数的各个参数的含义是什么,比如 coreSize, maxsize 等

21、线程的生命周期

22、多线程中的忙循环是什么

23、线程和进程有什么区别?进程间如何通讯,线程间如何通讯?

24、什么是多线程环境下的伪共享(false sharing)

25、同步和异步有何异同,在什么情况下分别使用他们?举例说明

26、启动一个线程是调用 run() 还是 start() 方法?start() 和 run() 方法有什么区别

27、调用start()方法时会执行run()方法,为什么不能直接调用run()方法

28、sleep() 方法和对象的 wait() 方法都可以让线程暂停执行,它们有什么区别

29、Java 中如何停止一个线程

30、stop() 和 suspend() 方法为何不推荐使用

31、如何在两个线程间共享数据

32、如何让正在运行的线程暂停一段时间

33、什么是线程组,为什么在Java中不推荐使用

34、你是如何调用 wait(方法的)?使用 if 块还是循环?为什么

35、有哪些不同的线程生命周期

36、线程状态,BLOCKED 和 WAITING 有什么区别


锁机制
1、说说线程安全问题

2、volatile 实现原理  参考 

答   http://www.importnew.com/23520.html

     https://www.jianshu.com/p/157279e6efdb

3、synchronize 实现原理 参考

答  http://www.importnew.com/29031.html

https://www.jianshu.com/p/19f861ab749e

4、synchronized 与 lock 的区别?Lock 接口比 synchronized 块的优势是什么?

5、什么场景下可以使用 volatile 替换 synchronized

6、CAS乐观锁

7、ABA问题

8、什么是乐观锁(Optimistic Locking)?如何实现乐观锁?如何避免ABA问题,乐观锁的业务场景

9、ReentrantLock 实现原理  参考

10、synchronized和ReentrantLock 有什么不同

11、什么是AQS? 参考

答 https://www.jianshu.com/p/c790f2b4891c

https://www.cnblogs.com/chengxiao/archive/2017/07/24/7141160.html

12、CAS的实现原理么?参考 

13、ReadWriteLock是什么?

答   https://blog.csdn.net/qq_19431333/article/details/70568478

       https://www.jianshu.com/p/9f98299a17a5

14、锁机制有什么用

15、解释以下名词:重排序,自旋锁,偏向锁,轻量级锁,可重入锁,公平锁,非公平锁,乐观锁,悲观锁

16、什么时候应该使用可重入锁

17、简述锁的等级方法锁、对象锁、类锁

18、Java中活锁和死锁有什么区别?

19、什么是死锁(Deadlock)?导致线程死锁的原因?如何确保 N 个线程可以访问 N 个资源同时又不导致死锁

20、死锁与活锁的区别,死锁与饥饿的区别

21、怎么检测一个线程是否拥有锁

22、如何实现分布式锁

23、有哪些无锁数据结构,他们实现的原理是什么

24、读写锁可以用于什么应用场景

25、有T1,T2,T3三个线程,怎么确保它们按顺序执行?怎样保证T2在T1执行完后执行,T3在T2执行完后执行

26、同步块内的线程抛出异常会发生什么

27、当一个线程进入一个对象的 synchronized 方法A 之后,其它线程是否可进入此对象的 synchronized 方法B

28、使用 synchronized 修饰静态方法和非静态方法有什么区别

29、如何从给定集合那里创建一个 synchronized 的集合

猜你喜欢

转载自blog.csdn.net/qingtian211/article/details/80755594