JAVA面试通关要点-基础篇-线程

Posted IAmJavaDeveloper

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JAVA面试通关要点-基础篇-线程相关的知识,希望对你有一定的参考价值。

一、基础篇

3.线程

1) 创建线程的方式及实现

继承Thread类创建线程类

(1)定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表           了线程要完成的任务。因此把run()方法称为执行体。

(2)创建Thread子类的实例,即创建了线程对象。

(3)调用线程对象的start()方法来启动该线程。

通过Runnable接口创建线程类

(1)定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方           法体同样是该线程的线程执行体。

(2)创建 Runnable实现类的实例,并依此实例作为Thread的target来创建                 Thread对象,该Thread对象才是真正的线程对象。

(3)调用线程对象的start()方法来启动该线程。

通过Callable和Future创建线程

(1)创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执           行体,并且有返回值。

(2)创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该             FutureTask对象封装了该Callable对象的call()方法的返回值。

(3)使用FutureTask对象作为Thread对象的target创建并启动新线程。

(4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值


采用实现Runnable、Callable接口的方式创见多线程时,

优势是:

线程类只是实现了Runnable接口或Callable接口,还可以继承其他类。

在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。

劣势是:

编程稍微复杂,如果要访问当前线程,则必须使用Thread.currentThread()方法。

使用继承Thread类的方式创建多线程时

优势是:

编写简单,如果需要访问当前线程,则无需使用Thread.currentThread()方法,直接使用this即可获得当前线程。

劣势是:

线程类已经继承了Thread类,所以不能再继承其他父类。


2) sleep() 、join()、yield()有什么区别


sleep()

  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()方法会使当前线程等待调用join()方法的线程结束后才能继续执行


3) 说说 CountDownLatch 原理

CountDownLatch 内部维护了一个整数n,n(要大于等于0)在==当前线程== 初始化CountDownLatch方法指定。当前线程调用 CountDownLatch的await()方法阻塞当前线程,等待其他调用CountDownLatch对象的CountDown()方法的线程执行完毕。 其他线程调用该CountDownLatch的CountDown()方法,该方法会把n-1,直到所有线程执行完成,n等于0,==当前线程==就恢复执行。


4) 说说 CyclicBarrier 原理


CyclicBarrier简介CyclicBarrier是一个同步辅助类,允许一组线程互相等待,直到到达某个公共屏障点(commonbarrierpoint)。因为该barrier在释放等待线程后可以重用,所以称它为循环的barrier。


5) 说说 Semaphore 原理


Semaphore直译为信号。实际上Semaphore可以看做是一个信号的集合。不同的线程能够从Semaphore中获取若干个信号量。当Semaphore对象持有的信号量不足时,尝试从Semaphore中获取信号的线程将会阻塞。直到其他线程将信号量释放以后,阻塞的线程会被唤醒,重新尝试获取信号量。


6) 说说 Exchanger 原理


当一个线程到达exchange调用点时,如果它的伙伴线程此前已经调用了此方法,那么它的伙伴会被调度唤醒并与之进行对象交换,然后各自返回。如果它的伙伴还没到达交换点,那么当前线程将会被挂起,直至伙伴线程到达——完成交换正常返回;或者当前线程被中断——抛出中断异常;又或者是等候超时——抛出超时异常。


7) 说说 CountDownLatch 与 CyclicBarrier 区别


(01) CountDownLatch的作用是允许1或N个线程等待其他线程完成执行;而CyclicBarrier则是允许N个线程相互等待。

(02) CountDownLatch的计数器无法被重置;CyclicBarrier的计数器可以被重置后使用,因此它被称为是循环的barrier。


8) ThreadLocal 原理分析


ThreadLocal提供了线程本地变量,它可以保证访问到的变量属于当前线程,每个线程都保存有一个变量副本,每个线程的变量都不同。ThreadLocal相当于提供了一种线程隔离,将变量与线程相绑定。


9) 讲讲线程池的实现原理


当提交一个新任务到线程池时,线程池的处理流程如下。

(1) 线程池判断核心线程池里的线程是否都在执行任务。如果不是,则创建一个新的工作

线程来执行任务。如果核心线程池里的线程都在执行任务,则进入下个流程。

(2) 线程池判断工作队列是否已经满。如果工作队列没有满,则将新提交的任务存储在这

个工作队列里。如果工作队列满了,则进入下个流程。

(3) 线程池判断线程池的线程是否都处于工作状态。如果没有,则创建一个新的工作线程

来执行任务。如果已经满了,则交给饱和策略来处理这个任务。


10) 线程池的几种方式


在Executors类里面提供了一些静态工厂,生成一些常用的线程池。

(1) newFixedThreadPool:创建固定大小的线程池。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。

(2) newCachedThreadPool:创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。

(3) newSingleThreadExecutor:创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

(4) newScheduledThreadPool:创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。

(5) newSingleThreadScheduledExecutor:创建一个单线程的线程池。此线程池支持定时以及周期性执行任务的需求。


11) 线程的生命周期


新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)5种状态



|more|
























以上是关于JAVA面试通关要点-基础篇-线程的主要内容,如果未能解决你的问题,请参考以下文章

#面试# 阿里大佬Java面试通关要点汇总集基础篇核心篇框架篇

Java面试通关要点问题汇总篇

Java面试通关要点汇总集

Java面试通关要点汇总集

文末福利Java面试通关要点框架篇

Java面试通关要点 汇总集最终版