关于并发十道常见面试题

发布时间:2024年01月16日

面试题一:线程中的start和run方法有什么区别

Java中线程是通过Thread类来实现的,每个线程都是通过特定的Thread对象所对应的run方法来完成

  1. start()方法来启动线程,真正的实现多线程,这时无需等待run()方法体代码执行完成,可以直接继续执行下面的代码,通过Thread类的start()方法来实现一个线程,这是此线程是处于就绪状态的,并没有运行,然后通过Thread类调用run()方法来完成运行操作
  2. run()?方法当作普通方法的方式调用。程序还是要顺序执行,要等待 run 方法体执行完毕后,才可继续执行下面的代码; 程序中只有主线程——这一个线程, 其程序执行路径还是只有一条, 这样就没有达到写线程的目的。

而start和run的区别可以比作汽车的钥匙和引擎,如果你没有钥匙的话就不会启动汽车

想要分清他们的区别还是要看代码更加直观:

可以看到run并没有创建线程,start创建了一个线程

面试题二:线程是如何通讯的?它的通讯方法有哪些?(说出你知道的所有通讯方法)

首先线程间的通信是指等多个线程之间操作同一份数据的时候,互相告知对方自己的状态,避免对同一变量进行争夺

在 Java 中,线程通讯的实现方法主要有以下几种:

  1. Object 类下的 wait()、notify() 和 notifyAll() 方法。
  2. Condition 类下的 await()、signal() 和 signalAll() 方法。
  3. LockSupport 类下的 park() 和 unpark() 方法。

Object 类下的 wait()、notify() 和 notifyAll() 方法:

wait():会让当前线程进入等待状态,并且释放所持有的锁

notify():随即唤醒一个等待该锁的其他线程,如果有多个线程都在等待这个对象的锁,这个方法只会唤醒其中一个线程。

notifyAll():这个方法会唤醒所有正在等待这个对象的锁的线程。

Condition 类下的 await()、signal() 和 signalAll() 方法

分别对应上述的wait、notify、notifyAll

LockSupport 类下的 park() 和 unpark() 方法

park():这方法会让当前线程进入等待,如果调用unpark()方法或者被线程终端,那么这个线程就i可以从park()方法返回

unpark(Thread thread):这个方法会让执行线程从park()方法返回

面试题三:说一下线程的生命周期

线程的生命周期主要分为五个阶段,NEW、RUNNABLE、BLOCAKED、WAITING、TIMED_WAITING、TERMINATED

  1. NEW(新建状态):new Thread() 时线程的状态。
  2. RUNNABLE(可运行/运行状态):调用 start() 方法后的状态。
  3. BLOCKED(阻塞状态):调用了 synchronized 加锁之后的状态。获得锁之后就从 BLOCKED 状态变成了 RUNNABLE 状态。
  4. WAITING(无时限等待状态):调用了 wait() 方法之后会进入此状态。
  5. TIMED_WAITING(有时限等待状态):调用了 sleep(long millis) 方法之后会进入此状态。
  6. TERMINATED(终止状态):线程任务执行完成之后就变成此状态。

面试题四:如何停止线程

自定义标识符:如定义一个flag变量true继续false退出

使用interrupt()方法:interrupt方法可以用来中断线程,但是并不会停止线程。它只是给线程设置一个中断标志。线程需要检查这个标志,然后决定是否停止执行。

使用stop()方法(已经放弃使用了不安全)

面试题五:wait()方法和sleep()方法有什么区别?

Wait和sleep都是可以用来暂停当前线程的执行,但是他们还是有一定区别:

  1. 所属类:wait是Object类中的,sleep是Thread类中的
  2. 锁处理:当线程执行wait时,会释放它当前持有的对象锁,进入等待状态。而执行sleep的时候不会释放锁
  3. 使用方式:wait必须在同步块或者同步方法(即synchronized)中调用,sleep任何地方都可以调用
  4. 使用场景:wait主要用于线程间通信,sleep用于暂停执行

面试题六:线程池相比于线程有什么优点?

线程池是一种管理和复用线程的机制,他预先创建一组线程,并且维护一个任务队列,当任务来的时候,会从线程池中选择线程去执行任务,而不是直接去创建一个线程。

线程池是一种管理和复用线程的机制,优点有以下几种:

  1. 线程重用(减低资源消耗):线程池可以重复利用已创建的线程,可以降低线程创建和销毁造成的消耗
  2. 提高响应速度:当任务到达的时候,如果线程池中有空闲的线程,那么这个任务可以立即得到执行,而不需要等待线程的创建
  3. 提供更强大的功能:线程池具备可拓展性,允许开大人员向其增加更多的功能。例如线程池的任务队列,使用任务队列可以存储更多的待执行的任务
  4. 提高系统的稳定性:线程池可以限制并发线程的数量,避免系统因为线程过多而导致资源耗尽或系统奔溃

面试题七:说下线程池创建参数都有哪些?它们都有哪些含义?

在Java中线程池是由ThreadPoolexecutor类实现的,其有多个参数:

  1. corePoolSize(核心线程数):这是线程池中能够同时执行线程的数量。即使线程处于空闲状态,核心线程也不会销毁
  2. maximumPoolSize(最大线程数):当任务队列已满且核心线程数以达到上限时,线程池会创建新的线程,直到达到最大线程数。
  3. keepAliveTime(空闲线程最大存活时间):当线程池中的线程数量大于核心线程且处于空闲的状态,那么在指定时间后,这个空闲线程就会被销毁
  4. unit(空闲线程存活时间单位):通常TimeUnit.SECONDS秒级。
  5. workQueue(工作队列):新任务提交后,会先进入到此工作队列中,任务调度时再从队列中取出任务。JDK提供四种任务队列:ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue和PriorityBlockingQueue
  • ArrayBlockingQueue:基于数组的有界阻塞队列,按照FIFO(先进先出)排序,新任务进来后会放到队尾,有界的数组可以防止资源耗尽问题。达能线程池中的线程数量达到了corePoolSize后再有新任务到来,则会将任务梵高该队列的队尾,等待被调度。如果队列已经是满的,则创建一个新线程,如果线程数量已经达到maxPoolSize,则会执行拒绝策略。
  • LinkedBlockingQueue:基于链表的无界阻塞队列(其实最大容量为Interger.MAX),按照FIFO排序。由于该队列的近似无界性,当线程池中线程数量达到corePoolSize后,再有新任务进来,会一直存入该队列,而基本不会去创建新线程直到maxPoolSize(很难达到Interger.MAX这个数),因此使用该工作队列时,参数maxPoolSize其实是不起作用的。
  • SynchronousQueue:一个不缓存任务的阻塞队列,生产者放入一个任务必须等到消费者取出这个任务。也就是说新任务进来时,不会缓存,而是直接被调度执行该任务,如果没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略
  • PriorityBlockingQueue:具有优先级的无界阻塞队列,优先级通过参数Comparator实现。

  1. threadFactory(线程工厂):可以用来设定线程名,是否为daemon(守护线程)线程
  2. handler(拒绝策略):这是当工作队列达到了最大限制,并且线程池中的线程数量也达到最大限制,这时如果有新任务提交过来,该如何处理。JDK提供了四种拒绝策略CallerRunsPolicy、AbortPolicy、DiscarPolicy和DiscardOldestPolicy
  • CallerRunsPolicy:在调用者线程中直接执行被拒绝任务的run方法,除非线程池已经shutdown,则直接抛弃任务
  • AbortPolicy:直接丢弃任务,抛出RejectedExecutionException异常
  • DiscarPolicy:直接抛弃任务,什么也不做
  • DiscarPolicy:抛弃进入队列最早的那个任务,然后尝试把这次拒绝的任务放入队列

面试题八:线程工厂有什么用?不设置线程工厂会怎样?

在Java中线程工厂是一个接口,主要用于创建新的线程,它通常与线程池一起使用,主要用于控制创建新线程时的一些行为,比如设置线程的优先级、名称等

如果在创建线程池的时候没有设置线程工厂,那么线程池会使用默认的线程工厂,默认的线程工厂会创建一个线程,为欸其设置线程池编号和线程编号组成的名称,设置它的优先级为正常优先级,且不是守护线程

面试题九:线程的优先级有什么用?如何设置线程池的优先级?

线程的优先级用证书表示,范围是1-10,数字越大优先级越高,线程的默认优先级为5

线程的优先级越高,表示它在竞争 CPU 资源时更有可能被调度执行。然而,线程优先级的具体行为在不同的操作系统和 Java 虚拟机实现中可能会有所不同。所以,线程优先级仅仅是给操作系统一个提示,告诉它应该优先调度哪个线程,但操作系统可能不会严格按照优先级来调度线程。

Java 中,线程的优先级由 Thread 类的 setPriority() 和 getPriority() 方法来设置和获取线程的优先级。

面试题十:说一下线程池的执行流程

线程池执行流程主要包括以下几个步骤:

  1. 提交任务:当提交一个任务到线程池的时候,线程池首先会判断核心线程池的线程是都都在执行任务,如果没有,则会创建一个新的工作来执行任务。如果核心线程池里的线程都在执行任务,则进入下一个流程
  2. 等待队列:线程池会判断等待队列是否已经满,如果没有满,则添加任务到等待队列中,如果满了,则进入下一个流程
  3. 非核心线程:线程池会尝试创建一个非核心的线程来执行任务,如果创建失败(达到最大线程数),则会拒绝任务
  4. 执行任务:线程池中的线程(无论是核心线程还是非核心线程)会执行任务,执行任务可能是新提交的任务,也可能是等待队列中的任务

文章来源:https://blog.csdn.net/loss_rose777/article/details/135621040
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。