线程池的工作原理?
核心线程比作正式员工
非核心线程比作外包员工
阻塞队列比作需求池
提交任务比作提需求
java线程池实现原理?
在线程池中存在几个概念:核心线程数、最大线程数、任务队列。
核心线程数指的是线程池的基本大小;
最大线程数指的是,同一时刻线程池中线程的数量最大不能超过该值;
任务队列是当任务较多时,线程池中线程的数量已经达到了核心线程数,这时候就是用任务队列来存储我们提交的任务。
ThreadPoolExecutor?
ThreadPoolExecutor最复杂的构造方法有7个参数
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
}
- corePoolSize: 核心线程数
- maximumPoolSize: 线程池允许存在的最大线程数量
- keepAliveTime: 非核心线程的存活时间
- unit: 非核心线程存活时间的单位
- workQueue: 任务队列,通常是阻塞队列,常见有ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue、PriorityBlockingQueue等
– ArrayBlockingQueue底层实现是数组,数组初始化需指定大小,因此为有界队列
– LinkedBlockingQueue底层实现是链表,默认大小是Integer.MAX_VALUE,因为过于大称之为无界队列
– SynchronousQueue是不存元素的阻塞队列,每个插入操作需等另一个线程调用了移除操作后,该线程才会返回,否则一直阻塞
– PriorityBlockingQueue按元素优先级排序的阻塞队列,优先级越高越先出队,是无界队列 - threadFactory:线程池工厂,用来创建线程
- RejectedExecutionHandler: 拒绝策略。任务队列满了且线程数量满了,线程池不会再接受新的任务。
– AbortPocily: 抛出异常,默认用这个
– CallerRunsPolicy: 交由提交任务的线程处理
– DiscardPolicy: 不处理,直接丢弃
– DiscardOldestPolicy: 丢弃在任务队列中等待时间最长的任务,后将该任务加入队列
ThreadPoolExecutor提交任务的方式
- execute(Runnable task)方法向线程池中提交一个任务,没有返回值
- submit(Runnable task),该方法虽然返回值对象是Future,但是使用Future.get()获取结果是null
- submit(Runnable task,T result),方法的返回值对象是Future,通过Future.get()获取具体的返回值时,结果与方法的第二个参数result相等
- submit(Callable task),该方法的参数是一个Callable类型的对象,方法有返回值
说说你对volatile的理解?
- 内存模型:cpu-高速缓存-主存。运算开始时,会将数据从主存复制一份到高速缓存使cpu可以从高速缓存存取数据,运算结束后再从高速缓存复制到主存。每个线程都有自己的高速缓存。
- 缓存一致性问题:多个线程的高速缓存同时得到了一份主存数据的副本,进行运算后,将各自的结果写回主存,未出现预期结果的问题
- 缓存一致性的解决方案:最著名的时Intel的MESI协议,它的核心思想是,当 CPU写数据时,如果发现操作的变量是共享变量,即在其他 CPU 中也存在该变量的副本,会发出信号通知其他 CPU 将该变量的缓存行置为无效状态,因此当其他 CPU需要读取这个变量时,发现自己缓存中缓存该变量的缓存行是无效的,那么它就会从内存重新读取。
- 并发编程的3个概念
– 原子性:要么都执行,要么都不执行
– 可见性:一个线程修改变量后其他线程能立即得到修改过后的值
– 有序性:程序按照代码的先后顺序执行,不会发生指令重排
— 指令重排:处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同 代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。
共享变量被volatile 修饰有两层效果
- 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的
- 禁止进行指令重排序。
如何终止一个运行中的线程?
- 使用interrupt()
**
* 正确停止线程---run()方法内没有sleep()或者wait()方法-未处理中断信号
*
* @author futao
* @date 2020/6/6
*/
public class StopThreadWithoutSleepWait implements Runnable {
@Override
public void run() {
unHandleInterrupt();
}
/**
* 未处理中断
*/
public void unHandleInterrupt() {
int num = 0;
//打印最大整数一半的范围内10000的倍数
while (num <= Integer.MAX_VALUE / 2) {
if (num % 10000 == 0) {
System.out.println(num + "是10000倍数");
}
++num;
}
System.out.println("任务执行完毕");
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new StopThreadWithoutSleepWait());
//启动线程
thread.start();
//增加子线程处于运行状态的可能性
Thread.sleep(500L);
//尝试中断子线程
thread.interrupt();
}
}
ThreadLocal的原理?
- 每个线程都有一个 ThreadLocalMap 类型的 threadLocals 属性。
- ThreadLocalMap 类相当于一个Map,key 是 ThreadLocal 本身,value 就是我们的值。
- ThreadLocal 适用于每个线程需要自己独立的实例且该实例需要在多个方法中被使用,也就是变量在线程间隔离,而在同一线程共享的场景。例如管理Connection,我们希望每个线程只使用一个Connection实例,这个时候用ThreadLocal就很合适
java线程有哪些状态?
- 初始(NEW):新创建了一个线程对象,但还没有调用start()方法。
- 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。
- 阻塞(BLOCKED):表示线程阻塞于锁。
- 等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
- 超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。
- 终止(TERMINATED):表示该线程已经执行完毕。