手记

线程面试题

1、守护线程是什么?

    守护线程是运行在后台的一种特殊的进程。它独立于控制终端并且周期性地执行某种任务或者等待处理某些发生的事件。在Java中垃圾回收线程就是特殊的守护线程。

2、创建线程的方式?

  • 继承Thread重写run方法

  • 实现Runnable接口

  • 实现Callable接口

3、Thread中的start和run方法的区别?

start方法用于启动线程,run()方法用于执行线程的运行时代码。run()可以重复调用,而start只能调用一次。

4、Thread类与Runnable有什么关系

  • Thread实现了Runnable接口的类,使得run支持多线程

  • 因为类的单一继承原则,推荐多使用Runnable接口

5、说一下Runnable和callable有什么区别?

Runnable没有返回值,callable可以拿到有返回值,callable可以看作是Runnable的补充

区别:前者无返回值,后者有返回值

前者无泛型,后者有泛型

前者的run方法无法向外抛出异常,后者的call方法可以向外排除异常

相同:都是函数式接口

6、线程有哪些状态?

new 新建  runnable 运行  waiting 无限期等待  time waiting等待指定时间重新被唤醒  blocked阻塞  Terminated 终止

7、sleep区别:

  • sleep是Thread类的方法,wait是Object类中定义的方法

  • sleep可以用于任何地方,而wait方法只能用于synchronized方法或者synchronized代码块

    只有获取锁才能释放锁

  • sleep只会让出CPU,不会释放锁,而wait不仅会让出CPU,还会释放以及占有的同步资源

  • sleep时间到会自动恢复,而wait可以使用notify、notifyall直接唤醒

8、notify和notifyall的区别:

notifyall,将全部线程由等待池移动到锁池,然后参与锁的竞争,竞争成功则继续执行。而notify只会唤醒一个线程,具体唤醒哪一个线程由虚拟机控制

9、线程池都有哪些状态?

  • running:这是最正常的状态,接受新的任务,处理等待队列中的任务

  • shutdown:不接受新的任务提交,但是会继续处理等待队列中的任务

  • stop:不接受新的任务提交,不再处理等待队列中的任务,中断正在执行任务的线程

  • tidying:所有任务都销毁了,workCount为0,线程池的状态在转换为tidying状态时,会执行钩子方法terminated

  • terminated

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

  • execute()只能执行Runnable类型的任务

  • submit()可以执行Runnable和Callable类型的任务

  • excute执行任务时,遇到异常直接抛出;submit不会直接排除,只有使用Future的get方法时才会抛出异常

11、在Java程序中怎么保证多线程的运行安全?

  • 方法一:使用安全类,比如java.util.conrrent类

  • 方法二:使用自动锁synchronized

  • 方法三:使用手动锁LOCK

12、什么是死锁?

    AB两个线程由于互持有对方需要的锁,而发生的阻塞线程

13、怎样防止死锁?

  • 尽量使用tryLock(long timeout,TimeUnit unit)的方法(ReentrantLock,ReentrantReadWriteLock),设置超时时间,超时可以退出防止死锁

  • 尽量使用Java.util.concurrent并发类代替自己手写锁

  • 尽量降低锁的使用粒度,尽量不要几个功能用同一把锁

  • 尽量减少同步的代码块

14、ThreadLocal是什么?有哪些使用场景?

    ThreadLocal为每个使用该变量的线程提高独立的变量副本,所以每一个线程都可以独立改变自己的副本,而不会影响其他线程所对应的副本

    ThreadLocal的经典使用场景是数据库连接和session管理

15、synchronized和volatile的区别?

  • volatile(vɑːlətl)是变量修饰符;synchronized是修饰符、方法、代码段

  • volatile仅能实现变量的修改可见性,不能保证互斥性;而synchronized则可以保证变量的修改可见性和互斥性

  • volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。

16、synchronized和ReentrantLock区别是什么?

ReentrantLock是Lock的一个实现类

  • ReentrantLock使用起来比较灵活,但是必须有释放锁的配合动作

  • ReentrantLock必须手动获取与释放锁,而synchronized不需要手动释放和开启锁

  • ReentrantLock只适用于代码块锁,而synchronized可用于修饰方法、代码块

17、FutureTask类是Runnable接口的一个实现类,它的构造方法可以接受Callable类型的对象,因此在构造线程的方法如下:

FutureTack<V> ft = new FutureTask<V>(()->{  //todo;需要一个返回值

});  new Thread(ft).start; ft.get//获取结果

主要用在需要长时间计算的任务中,即为长时间计算的任务开辟一个线程,让这个任务在后台完成,达到不阻塞主线程的目的,当任务完成后通过get方法调用计算的结果。可以通过ft.isDone()方法判断任务是否完成,如果任务还未完成就调用get方法,将产生阻塞,同理,如果在主线程中执行该代码,也会阻塞,直至代码运行完成。

同一个FutureTask对象只会执行一次,因为他执行的任务比较复杂 多次执行十分消耗资源

18.Lock

public interface Lock{
    void lock();
    void lockInterruptibly() throws InterruptedException;
    boolean tryLock();
    boolean tryLock(long time,TimeUnit unit) throws InterruptedException
    void unlock();
    Condition newCondition();
}

java.util.concurrent.locks包下常用的类和接口Lock

由于在前面讲到如果采用Lock,必须主动去释放锁,并且在发生异常时,不会自动释放锁。因此一般来说,使用Lock必须在try{}catch{}块中进行,并且将释放锁的操作放在finally块中进行,以保证锁一定被被释放,防止死锁的发生

tryLock()方法是有返回值的,它表示用来尝试获取锁,如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false,也就说这个方法无论如何都会立即返回。在拿不到锁时不会一直在那等待。

Lock lock = ...;
if(lock.tryLock()) {     
 try{       
         //处理任务       
 }catch(Exception ex){           
        
        }finally{        
         lock.unlock();   //释放锁    
          }
    }else {   
          //如果不能获取锁,则直接做其他事情
  }

总结来说,Lock和synchronized有以下几点不同:

  1)Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;

  2)synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;

  3)Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;

  4)通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。

  5)Lock可以提高多个线程进行读操作的效率。

  在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。

可重入锁: 上述代码中的两个方法method1和method2都用synchronized修饰了,假如某一时刻,线程A执行到了method1,此时线程A获取了这个对象的锁,而由于method2也是synchronized方法,假如synchronized不具备可重入性,此时线程A需要重新申请锁。但是这就会造成一个问题,因为线程A已经持有了该对象的锁,而又在申请获取该对象的锁,这样就会线程A一直等待永远不会获取到的锁。

可中断锁:顾名思义,就是可以相应中断的锁。

  在Java中,synchronized就不是可中断锁,而Lock是可中断锁。

  如果某一线程A正在执行锁中的代码,另一线程B正在等待获取该锁,可能由于等待时间过长,线程B不想等待了,想先处理其他事情,我们可以让它中断自己或者在别的线程中中断它,这种就是可中断锁

  在前面演示lockInterruptibly()的用法时已经体现了Lock的可中断性。

公平锁:即尽量以请求锁的顺序来获取锁。比如同是有多个线程在等待一个锁,当这个锁被释放时,等待时间最久的线程(最先请求的线程)会获得该所,这种就是公平锁。

  非公平锁即无法保证锁的获取是按照请求锁的顺序进行的。这样就可能导致某个或者一些线程永远获取不到锁。

  在Java中,synchronized就是非公平锁,它无法保证等待的线程获取锁的顺序。

  而对于ReentrantLock和ReentrantReadWriteLock,它默认情况下是非公平锁,但是可以设置为公平锁。


1人推荐
随时随地看视频
慕课网APP