线程面试题

线程的生命周期和线程的几种状态

线程的生命周期包含5个阶段,包括:新建、就绪、运行、阻塞和死亡状态。

 阻塞分为三种:

1.等待阻塞,运行的线程执行了object的wait方法,该线程会释放占用的所有锁资源,jvm会把该线程放到“等待池中”,不能自动唤醒,必须依靠其他线程调用notify或者notifyall方法

2.同步阻塞,运行的线程在获取同步锁Synchronized时,同步锁被其他线程占用,jvm会把该线程放到“锁池中”

3.其他阻塞,运行线程调用了sleep方法或者join方法,或者发出I/O请求,jvm会把线程设置为阻塞状态,当sleep超时,join终止或者超时,I/O处理完毕,线程会重新转到就绪状态。sleep是thread类方法。

 

sleep、wait、yield、join区别

1.锁池

所有需要竞争同步锁的线程都会放到锁池中(也就是被Synchronized修饰没有拿到锁的线程),当同步锁释放后,锁池中等待的线程要去竞争同步锁,当某个线程得到后就会进入就绪队列等待cpu分配资源。

2.等待池

当某个线程拿到Synchronized同步锁后,调用wait方法,该线程会释放占用的所有锁资源,jvm会将线程放到等待池中,等待池的线程是不会区竞争同步锁的,只有调用了notify或者notifyall方法才会开始去竞争锁,notify是随机从等待池中释放一个线程到锁池中,notifyall是释放所有的线程到锁池中。

1.sleep是tread线程的静态方法,wait是object的方法

2.sleep不会释放锁,而wait会释放锁,而且会进入等待队列中。

sleep是将cpu的执行资格和执行权释放,规定时间内不再运行此程序,当规定时间结束后在取回cpu资源,参与cpu的调度。

3.sleep不依赖Synchronized(在哪里都可用调用),wait需要依赖Synchronized。

4..sleep不需要被唤醒,wait需要唤醒。(现在也有带时间的一个重载方法)

5.sleep用于当前线程的休眠,或者轮询暂停操作,wait用于多线程之间的通信。

6.sleep会让出cpu的执行时间,强制上下文的切换,而wait不一定,wait还有机会重新竞争锁继续执行。

yield执行后,直接进去就绪状态,马上释放cpu的执行权,但任然保留着cpu的执行资格,所以有可能在cpu下次进入线程时还会让该线程继续执行。

join,执行后进入阻塞状态,阻塞的是调用该线程的线程,直到该线程执行结束。列如B线程调用了A线程,a.join方法阻塞的是b线程。

 

线程安全问题

因为堆是共享内存,可用被所有线程访问,当多个线程访问同一个对象时,如果不用进行额外的同步控制或其他的协调操作,调用这个线程都会获得正确的结果,我们就说这个对象时线程安全的

 

守护线程的理解

thred.setDaemon(true)必须在thred.start()之前设置

守护线程产生的新线程也是守护线程,不能访问固有资源,比如读写操作,计算逻辑,因为用户线程结束,守护线程就会中断。

守护线程是为非守护线程提供服务的线程,守护线程是所有jvm中用户线程的守护线程,一旦用户线程全部结束,守护线程就会被中断。

GC回收线程就是一个最经典的守护线程,当程序中不再有任何的线程,程序不再产生垃圾,垃圾回收线程就没有无事可做,垃圾回收线程就会自动离开。

threadlocal的原理和使用场景

每一个thread对象都包含者一个threadlocalMap类型的成员变量threadlocals,存储者本线程所有的threadlocal对象及其对应的值.   

 

为什么用线程池,解释下线程池的参数?

1.降低资源消耗:提高线程利用率,降低创建和销毁线程的消耗。

2.提高响应速度:任务来了,直接有线程可用可执行,而不是先创建线程再执行。

3.提高线程的可管理性:线程是稀缺资源,使用线程可以统一分配调优监控。

一、corePoolSize 线程池核心线程数

线程池中会维护一个最小的线程数量,即使这些线程处理空闲状态,他们也不会被销毁,而是一种常驻线程。(线程池回收而回收)

二、maximumPoolSize 线程池最大线程数量

线程池最大线数它与核心线程数相对应:比如我常驻线程是5个,但是我最大线程达到10个(这10个线程只处于高峰期,平时5个就够用了),我们就设置一个高峰期的最大线程数。

三、keepAliveTime 空闲线程存活时间:设置一个空闲时间,如果达到了时间就会回收线程

四、unit 空闲线程存活时间单位

五、workQueue 工作队列:核心数5个线程都在工作,此时在来一个线程,会先放在队列里面,如果队列满了就会创建最大线程maximumPoolSize 。

六、threadFactory 线程工厂: 用来生产线程执行任务的

 七、handler 拒绝策略    最大线程数满了就会执行拒绝策越 1.关闭线程池,调用shutdown等方法 2.达到最大线程数 

 

线程池处理的流程

1.线程池执行任务2.判断核心线程数是否已满,未满创建线程执行任务3.满了,判断工作队列是否已满,未满将任务放到队列中4.满了判断最大线程数是否已满,未满创建临时线程执行5满了,根据拒绝策越处理任务。

 

线程池中阻塞队列的作用?为什么先添加队列而不是创建最大线程?

当普通队列有限长度的缓存区满了,是无法保留当前任务的而阻塞队列通过阻塞可用保留当前任务进入队列,是线程进入wait状态,释放cpu资源,并且自带阻塞和唤醒的功能,无任务时,线程池利用阻塞队列的take方法挂起,从而维护核心线程的存活,不会一直占cpu的资源。

 

在创建线程的时候,会去获取全局锁,这个时候其他的线程就会阻塞,影响整体效率。

公司招人,平时工作量10个人就够了,公司就会招10个人,如果有一天工作量大了,就先会把工作积压一下,等10个人有某个人空闲了去做,因为平时的工作量10个人刚好能处理,如果因为某一天的工作量超标区招聘人的话,等闲下来的时候会浪费资源。万一工作量长时间超过了10个人的工作量,就去找外包,因为招人成本过高,并且工作量过高并不是常态,如果正式工和临时工都完成不了就会执行拒绝策越。

线程池的复用原理

线程池将任务和线程进行解耦,拜托了new一个线程必须对应一个任务的限制。

在线程池中,同一个线程可以从阻塞队列中不断获取新任务来执行:其核心原理在于线程池对tread进行了封装,并不是每次执行任务就会调用tread.start()方法,而是每个线程区执行一个循环任务,在这个循环任务中不断的检查有没有任务需要执行,如果有就直接执行,也就是调用 了任务中的run方法,将run方法当作一个普通方法执行,通过这种方式使用固定的线程将所有的run方法串联起来。

 

posted @ 2021-03-07 06:38  阿布v  阅读(139)  评论(0)    收藏  举报