1.Java 线程有哪几种创建方式?各自优缺点是什么?
三种方式,分别是:
继承Thread,实现简单,java单继承,不可以在继承其他类;
实现Runnable解耦任务和线程,推荐使用;
实现Callable可以获取线程执行结果,配合futrue使用;
2.线程有哪几种状态?sleep() 和 wait() 有什么区别?
六种状态,分别是new(任务刚创建)、runnable(执行中)、blocked(阻塞中)、waiting(无限等待)、timed-waiting(有限等待)、terminated(执行结束,线程杀死)
sleep():线程休眠,不释放锁,到时间自动唤醒;
wait():线程等待,释放锁,需要notify()/notifyAll()被唤醒;
3.为什么要用线程池?
1、避免重复创建或销毁线程,减少开销;
2、控制线程并发数量,避免服务器崩溃;
3、统一管理线程,方便监控;
4.线程池7大参数?
核心线程数、最大线程数、非核心线程空闲时、时间单位、阻塞队列、线程工厂、拒绝策略
常用参数含义
corePoolSize : 核心线程数线程数定义了最小可以同时运行的线程数量。
maximumPoolSize : 当队列中存放的任务达到队列容量时,当前可以同时运行的线程数量变为最大线程数。
workQueue: 当新任务来的时候会先判断当前运行的线程数量是否达到核心线程数,如果达到的话,新任务就会被存放在队列中
keepAliveTime:当线程池中的线程数量大于 corePoolSize 的时候,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了 keepAliveTime才会被回收销毁;
unit : keepAliveTime 参数的时间单位。
threadFactory :executor 创建新线程的时候会用到。
handler :饱和策略。关于饱和策略下面单独介绍一下。
5.线程池状态
Running → ShutDown → Stop → Tidying → Terminated
6.线程池监控与调优
看:activeCount、queue size、completedTaskCount
CPU 密集型:核心线程数 = CPU 核心数 + 1
IO 密集型:核心线程数 = 2 * CPU 核心数(或更高)
7.任务执行流程
任务提交,检查核心线程是否有空余,有直接执行;没有放入阻塞队列等待;若队列也满了,则新开一个线程执行,直到最大线程数。已达到最大线程数量则执行拒绝策略。
8.常见阻塞队列
ArrayBlockingQueue:有界数组队列
LinkedBlockingQueue:无界 / 有界链表队列(容易 OOM)
SynchronousQueue:不存元素,直接移交
PriorityBlockingQueue:优先级队列
9.4种拒绝策略
1、AbortPolicy:直接抛出异常(e bao t pao le sei)
2、CallerRunsPolicy:创建线程自己执行(kao ler runs )
3、DiscardOldestPolicy:将队列中最老的任务丢弃(dis car d o dei s t )
4、DiscardPolicy:直接丢弃任务
10.Executors 工具类的坑
FixedThreadPool:队列无界 → OOM
SingleThreadExecutor:队列无界 → OOM
CachedThreadPool:max 无限 → 线程暴增 → OOM
生产环境禁止用 Executors,必须手动 new ThreadPoolExecutor
Executors 是方便但不安全的工具类,因为队列无界或线程数无上限,高并发会 OOM,所以生产必须手动创建 ThreadPoolExecutor。
11.synchronized 可以锁什么?
修饰实例方法:锁当前对象
修饰静态方法:锁当前类的 Class 对象
修饰代码块:锁括号里的对象
12.锁升级过程
偏向锁 → 轻量级锁(自旋锁) → 重量级锁(OS 互斥锁)
无竞争:偏向锁、轻度竞争:轻量级锁、激烈竞争:重量级锁
13.死锁四个必要条件
互斥:资源同一时间只能一个线程用
请求与保持:拿着锁还去请求别的锁
不可剥夺:锁不能被强行抢走
循环等待:线程之间形成环路等待
14.volatile 有哪两个作用?能不能保证原子性?
保证可见性,一个线程修改完目标,其他线程立马可以看到
禁止指令重排序(防止半初始化对象)
不能保证原子性,不能替代锁
15.ThreadLocal 用来干嘛?使用时要注意什么?
threadlocal每个线程独立副本,线程安全
适用:用户信息、会话、事务上下文
使用完必须 remove(),否则会内存泄漏
