Java面试题 第三部分 线程/线程池/JUC并发库

线程
1 Java线程的状态(join同步化,阻塞调用join方法的主线程)

NEW: 新建状态,线程对象已经创建,但尚未启动
RUNNABLE:就绪状态,可运行状态,调用了线程的start方法,已经在java虚拟机中执行,等待获取操作系统资源如CPU,操作系统调度运行。
BLOCKED:堵塞状态。线程等待锁的状态,等待获取锁进入同步块/方法或调用wait后重新进入需要竞争锁
WAITING:等待状态。等待另一个线程以执行特定的操作。调用以下方法进入等待状态。 Object.wait(), Thread.join(),LockSupport.park
TIMED_WAITING:线程等待一段时间。调用带参数的Thread.sleep, objct.wait,Thread.join,LockSupport.parkNanos,LockSupport.parkUntil
TERMINATED:进程结束状态。
在这里插入图片描述

2 进程和线程的区别,进程间如何通讯,线程间如何通讯

计算机分配资源的最小单元(进程):管道PIPE,命名管道,Socket通信,共享内存通信,信号量
任务调度的基本单元(线程):同步Sychronied,wait-notify,join,管道通信,while轮询

3 什么是threadlocal

ThreadLocal:线程局部的变量; 每个Thread的对象都有一个ThreadLocalMap,当创建一个ThreadLocal的时候,就会将该ThreadLocal对象添加到该Map中,其中键就是ThreadLocal,值可以是任意类型。包含set()和get()方法。

4 同一个线程连续start两次之后的结果是什么样子

Java的线程是不允许启动两次的,第二次调用必然会抛出IllegalThreadStateException,这是一种运行时异常,多次调用start被认为是编程错误。

5 守护线程是什么?守护线程是怎么退出的?

守护线程是在程序运行时提供后台服务的线程,不属于程序运行中不可或缺的部分。 当程序中所有非守护线程结束时,程序也就终止,同时杀死所有的守护线程。

线程池
1 线程池有哪些参数?分别有什么用?
线程池参数可以根据是否与核心线程相关分为两个部分

非核心线程相关:
keepAliveTime:线程空闲时间,除了核心线程外其他线程最大空闲时间
maxPoolSize:最大线程数,rejectedExecutionHandler执行的临界点

核心相关:
corePoolSize:核心线程数 一直存活的线程,初始阶段线程数<核心线程,新任务创建新线程,不复用已有空闲线程 当线程数小于核心线程数时,即使有线程空闲,线程池也会优先创建新线程处理
allowCoreThreadTimeout:允许核心线程超时,上限keepAliveTime

其他两个参数
queueCapacity:任务队列容量(阻塞队列)
rejectedExecutionHandler:任务拒绝处理器
两种情况会拒绝处理任务:
(1)当线程数已经达到maxPoolSize,并且队列已满,会执行reject处理(2)当线程池被调用shutdown()后,新任务到达

ThreadPoolExecutor类有几个内部实现类来处理这类情况:
AbortPolicy 丢弃任务,抛运行时异常
CallerRunsPolicy 执行任务
DiscardPolicy 忽视,什么都不会发生
DiscardOldestPolicy 从队列中踢出最先进入队列
实现RejectedExecutionHandler接口,可自定义处理器

线程池工作流程

  1. 判断线程池里的核心线程是否都在执行任务,如果不是(核心线程空闲或者还有核心线程没有被创建)则创建一个新的工作线程来执行任务。如果核心线程都在执行任务,则进入下个流程。
  2. 线程池判断工作队列是否已满,如果工作队列没有满,则将新提交的任务存储在这个工作队列里。如果工作队列满了,则进入下个流程。
  3. 判断线程池里的线程是否都处于工作状态,如果没有,则创建一个新的工作线程来执行任务。如果已经满了,则交给饱和策略来处理这个任务。

2 怎样设置线程池的大小(线程等待时间能被其他线程充分利用

最佳线程数目 = ((线程等待时间+线程CPU时间)/线程CPU时间 )* CPU数目
最佳线程数目 = (线程等待时间与线程CPU时间之比 + 1)* CPU数目
注意这里配置的线程池大小是在CPU充分利用前提下最大支持的线程数,具体配置线程数的时候需要根据业务具体决定

要想合理的配置线程池的大小,首先得分析任务的特性,可以从以下几个角度分析:

  1. 任务的性质:CPU密集型任务、IO密集型任务、混合型任务。
  2. 任务的优先级:高、中、低。
  3. 任务的执行时间:长、中、短。
  4. 任务的依赖性:是否依赖其他系统资源,如数据库连接等。

3 Java线程池的实现原理

实现原理: 内部状态借助AtomicInteger变量ctl(标记线程数量和线程池当前状态)

  1. RUNNING:-1 <<COUNT_BITS,即高3位为111,该状态的线程池会接收新任务,并处理阻塞队列中的任务;
  2. SHUTDOWN: 0 <<COUNT_BITS,即高3位为000,该状态的线程池不会接收新任务,但会处理阻塞队列中的任务;
  3. STOP : 1 <<COUNT_BITS,即高3位为001,该状态的线程不会接收新任务,也不会处理阻塞队列中的任务,而且会中断正在运行的任务;
  4. TIDYING : 2 <<COUNT_BITS,即高3位为010;
  5. TERMINATED: 3 <<COUNT_BITS,即高3位为011;

4 数据库连接池实现原理

连接池基本的思想是在系统初始化的时候,将数据库连接作为对象存储在内存中,当用户需要访问数据库时,并非建立一个新的连接,而是从连接池中取出一个已建立的空闲连接对象。使用完毕后,用户也并非将连接关闭,而是将连接放回连接池中,以供下一 个请求访问使用。而连接的建立、断开都由连接池自身来管理。同时,还可以通过设置连接池的参数来控制连接池中的初始连接数、连接的上下限数以及每个连接的最大使用次数、最大空闲时间等等。也可以通过其自身的管理机制来监视数据库连接的数量、使 用情况等。

5 sleep和wait,join,yield

sleep、yield方法是静态方法;作用的是当前执行的线程;
yield方法释放了cpu的执行权,但是依然保留了cpu的执行资格。
wait释放CPU资源,同时释放锁;
sleep释放CPU资源,但不释放锁;

6 如何实现要给简单的线程池

  1. 准备一个任务容器
  2. 一次性启动10个 消费者线程
  3. 刚开始任务容器是空的,所以线程都wait在上面。
  4. 直到一个外部线程往这个任务容器中扔了一个“任务”,就会有一个消费者线程被唤醒notify
  5. 这个消费者线程取出“任务”,并且执行这个任务,执行完毕后,继续等待下一次任务的到来。
  6. 如果短时间内,有较多的任务加入,那么就会有多个线程被唤醒,去执行这些任务。
    Ⅰ 任务容器List tasks;
    Ⅱ 自定义线程类run方法
{
	while(true) {
		 sychronized(tasks) {
		 	 while(Empty(tasks)) {
		 	 	 tasks.await(); 
		 	 }
		 	 Runnable runnable = tasks.removeLast();
		 	 tasks.notifyAll(); 
		 }
		 runnable.run(); 
   }
}

tasks的add和初始化方法也需要sychronied方法修饰,先初始化空跑线程再添加任务tasks中

Java同同步步工工具具类类
1 countdowmlatch,cyclebarrier,semaphore

countdownlatch:线程计数器,提高线程的并发性,并在主线程中确保需要等待的线程任务全部完成
cyclebarrier:类似与countdownlatch,countdown的点设置在线程执行任务的过程中,当所有线程都阻塞在barrier点的时候,才继续并发的往下执行。
semaphore:类似与一种令牌桶的机制,线程在执行任务前需要在令牌桶中获取足量的semaphore才能继续执行,否则阻塞等待

2 AtomicInteger,为什么要用CAS而不是synchronized?

AtomicInteger通过CAS原子方法操作避免了i++非原子操作操作导致的数据一致性问题。 在竞争条件下会阻塞等待资源,如果允许竞争不到资源返回失败,就可以使用cas减少阻塞时间。

3 java中的同步容器 (基于Synchronized的设计)

  1. Vector、Stack(继承了Vector)、HashTable
  2. Collections类中提供的静态工厂方法创建的类 同同步步案案例例

4 怎么实现一个线程安全的计数器?

AtomicInteger(volatile+CAS)&Sychronized (incrementAndGet)&LongAdder

fork&join算法
1 fork/join(分支递归,类似归并排序算法的思想

fork分割,join阻塞等待执行结果
1.任务分割
2.多线程执行任务并合并结果(future模式) fork切分,join合并 而使用
ForkJoinPool时,就能够让其中的线程创建新的任务,并挂起当前的任务,此时线程就能够从队列中选择子任务执行。

ForkJoinPool组成部分
ForkJoinPool:管理者,用于工作线程的创建和激活,任务队列的创建和分配也是由ForkJoinPool执行的
ForkJoinWorkerThread:工作线程,用于执行任务的线程,每个工作线程有一个自己WorkQueue双端队列用于存储需要执行的任务列表
ForkJoinTask:包含两个子类,RecursiveTask和RecursiveAction,分任务别代表有返回值的任务和无返回值的任务,compute方法用于执行逻辑或任务拆分
WorkQueue: 双端队列维护线程需要执行的任务信息 工工作作窃窃取取算算法法:工作线程对应的任务队列为空,无任务执行时随机从其他的工作线程对应的任务队列的尾部获取任务执行
ps:任务采用后进先出的策略放入到双端队列头部,工作线程每次从双端队列的头部获取任务执行,任务线程对于自己的任务队列执行调用push和pop接口放入和提取任务,窃取任务时调用qita队列的take方法获取队列尾部任务,当前仅当队列只有一个节点的时 候pop和take方法会产生冲突

2 一个任务分成十个任务,最后汇总计算,不能用fork/join

(1)任务分割
(2)分派执行
(3)执行结果汇总 并行提交,阻塞等待全任务执行结果,最后进行数据汇总

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值