【面霸系列 - 2】2022年初级java多线程精选10道题,必问题

在这里插入图片描述

2022初级java
难度:★★
作者建议:初级多线程,必问题

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

  • start()方法来启动线程,真正实现了多线程运行。这时无需等待run方法体代码执行完毕,可以直接继续执行下面的代码;通过调用Thread类的start()方法来启动一个线程, 这时此线程是处于就绪状态, 并没有运行。 然后通过此Thread类调用方法run()来完成其运行操作的, 这里方法run()称为线程体,它包含了要执行的这个线程的内容, Run方法运行结束, 此线程终止。然后CPU再调度其它线程。
  • run()方法当作普通方法的方式调用。程序还是要顺序执行,要等待run方法体执行完毕后,才可继续执行下面的代码; 程序中只有主线程——这一个线程, 其程序执行路径还是只有一条, 这样就没有达到写线程的目的。

2、 什么是多线程,多线程的优劣?

定义: 多线程是指程序中包含多个流,即在一个程序中可以同时进行多个不同的线程来执行不同的任务

优点

        可以提高CPU的利用率,在多线程中,一个线程必须等待的时候,CPU可以运行其它线程而不是等待,这样就大大提高了程序的效率,也就是说单个程序可以创建多个不同的线程来完成各自的任务。
缺点:

  • 线程也是程序,线程也需要占内存,线程也多内存也占的也多。
  • 多线程需要协调和管理,所以需要CPU跟踪线程。
  • 线程之间共享资源的访问会相互影响,必须解决禁用共享资源的问题。

3、 wait,notify,notifyAll,sleep,join,yield 的作用

  • wait 调用该方法的线程进入 WAITING 状态,只有等待另外线程的通知或被中断才会返回,需要注意的
    是调用 wait()方法后,会释放对象的锁。因此,wait 方法一般用在同步方法或同步代码块中。
  • sleep 导致当前线程休眠,与 wait 方法不同的是 sleep 不会释放当前占有的锁,sleep(long)会导致
    线程进入 TIMED-WATING 状态,而 wait()方法会导致当前线程进入 WATING 状态
  • yield 会使当前线程让出 CPU 执行时间片,与其他线程一起重新竞争 CPU 时间片。一般情况下,
    优先级高的线程有更大的可能性成功竞争得到 CPU 时间片,但这又不是绝对的,有的操作系统对
    线程优先级并不敏感。
  • interrupt 中断一个线程,其本意是给这个线程一个通知信号,会影响这个线程内部的一个中断标识位。这
    个线程本身并不会因此而改变状态(如阻塞,终止等)。
  • Join 方法,等待其他线程终止,在当前线程中调用一个线程的 join() 方法,则当前线程转为阻塞
    状态,回到另一个线程结束,当前线程再由阻塞状态变为就绪状态,等待 cpu 的宠幸。
  • notify Object 类中的 notify() 方法,唤醒在此对象监视器上等待的单个线程,如果所有线程都在此对象上等待,则会选择唤醒其中一个线程,选择是任意的,并在对实现做出决定时发生,线程通过调用其中一个 wait() 方法,在对象的监视器上等待,直到当前的线程放弃此对象上的锁定,才能继续执行被唤醒的线程,被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争。类似的方法还有 notifyAll() ,唤醒再次监视器上等待的所有线程。

4、如何创建线程?

  • 继承 继承 Thread

  • 实现 实现 Runnable 接口

5、四种线程池

        1、newCachedThreadPool:用来创建一个可以无限扩大的线程池,适用于负载较轻的场景,执行短期异步任务。(可以使得任务快速得到执行,因为任务时间执行短,可以很快结束,也不会造成cpu过度切换)

        2、newFixedThreadPool:创建一个固定大小的线程池,因为采用无界的阻塞队列,所以实际线程数量永远不会变化,适用于负载较重的场景,对当前线程数量进行限制。(保证线程数可控,不会造成线程过多,导致系统负载更为严重)

        3、newSingleThreadExecutor:创建一个单线程的线程池,适用于需要保证顺序执行各个任务。

        4、newScheduledThreadPool:适用于执行延时或者周期性任务。

6、线程 线程生命周期 生命周期( 状态)

        当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。在线程的生命周期中,它要经过新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)5 种状态。

7、ThreadLocal了解吗?

        可以在整个线程存活的过程中线程安全的随时取用,极大地方便了一些逻辑的实现。最常见的的点就是httpservletrequest 的原理,他即使再多线下使用getparmeter方法,也是线程安全的。

        原理就是在每个线程里面有threadLocals属性。这个属性中维护了一个map。在调用get、set方法的时候,是从自身线程的map拿去值。而且这个map使用的是ThreadLocalMap 优点就是在每个线程结束的时候,可以更好的垃圾清理。

8、synchronized和ReentrantLock的区别?

        synchronized是Java语言的关键字,基于JVM实现。在JDK1.6锁优化以前,synchronized的性能比ReenTrantLock差很多。但是JDK6开始,增加了适应性自旋、锁消除等,两者性能就差不多了。

        ReentrantLock 比 synchronized 增加了一些高级功能,如等待可中断、可实现公平锁、可实现选择性通知。

9、如果你提交任务时,线程池队列已满,这时会发生什么

        如果你使用的LinkedBlockingQueue,也就是无界队列的话,没关系,继续添加任务到阻塞队列中等待执行,因为LinkedBlockingQueue可以近乎认为是一个无穷大的队列,可以无限存放任务;如果你使用的是有界队列比方说ArrayBlockingQueue的话,任务首先会被添加到ArrayBlockingQueue中,ArrayBlockingQueue满了,则会使用拒绝策略RejectedExecutionHandler处理满了的任务,默认是AbortPolicy。

10、线程池拒绝策略

  • AbortPolicy – 当任务添加到线程池中被拒绝时,它将抛出 RejectedExecutionException 异常。
  • CallerRunsPolicy – 当任务添加到线程池中被拒绝时,会在线程池当前正在运行的Thread线程池中处理被拒绝的任务。
  • DiscardOldestPolicy – 当任务添加到线程池中被拒绝时,线程池会放弃等待队列中最旧的未处理任务,然后将被拒绝的任务添加到等待队列中。
  • DiscardPolicy – 当任务添加到线程池中被拒绝时,线程池将丢弃被拒绝的任务。

猜你喜欢

转载自blog.csdn.net/qq_30285985/article/details/122462935