线程thread

  1. 什么是线程 什么是进程
    答:进程 相当于一个加载到内存当中静态资源文件, 线程 是进程中的某个实体 是cup 调度和分派的基本单位
    并发属于逻辑性定性,并行属于物理级别

  2. 线程的实现方式 ()
    Runable 接口 Thread 类 ,callable 接口
    callable&& Future 能有返回值是因为 Futuretask 实现 RunnableFuture 接口 会去执行 run 方法
    用 volatile 修饰的 state 这个状态值 去判断执行操作(4中流程 看源码 状态执行流程) ,最终去执行call 方法 通过set ()方法去执行
    set() 去设置结果值 (用cas 比较自旋的方式去修改状态值 通知其他线程不用去获取值去塞了) 然后通过get 获取
    get 方法中也是去跟据线程状态去阻塞等待 ,利用cas去判断状态值 最总等于 NORMAL才返回

四种流程
* NEW -> COMPLETING -> NORMAL
* NEW -> COMPLETING -> EXCEPTIONAL
* NEW -> CANCELLED
* NEW -> INTERRUPTING -> INTERRUPTED

  1. 线程的生命周期和状态

在这里插入图片描述在这里插入图片描述
4. 线程池
线程池分为2种 普通线程池 (ThreadPoolExecutor)和定时线程池 (ScheduledThreadPoolExecutor 他还继承了前者)
面试的时候会问到 四种

一、四种线程池

Java通过Executors提供四种线程池,分别为
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。 LinkedBlockingQueue
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。LinkedBlockingQueue
newScheduledThreadPool 创建一个可定期或者延时执行任务的定长线程池,支持定时及周期性任务执行。 DelayQueue
newCachedThreadPool 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。 SynchronousQueue

二、核心类

四种线程池本质都是创建ThreadPoolExecutor类,ThreadPoolExecutor构造参数如下
int corePoolSize, 核心线程大小
int maximumPoolSize,最大线程大小
long keepAliveTime, 超过corePoolSize的线程多久不活动被销毁时间
TimeUnit unit,时间单位
BlockingQueue workQueue 任务队列
ThreadFactory threadFactory 线程池工厂
RejectedExecutionHandler handler 拒绝策略

三、阻塞队列

ArrayBlockingQueue :一个由数组结构组成的有界阻塞队列 (数组+锁)
LinkedBlockingQueue :一个由链表结构组成的有界阻塞队列 (链表+锁)(常用)
PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列 (堆+锁)
DelayQueue: 一个使用优先级队列实现的无界阻塞队列 (   Priorityqueue+锁)
						Priorityqueue:heap=完全二叉树+特性(小根堆:根比儿子小,大根堆:根比儿子大)
SynchronousQueue: 一个不存储元素的阻塞队列(常用)

LinkedTransferQueue: 一个由链表结构组成的无界阻塞队列
LinkedBlockingDeque: 一个由链表结构组成的双向阻塞队列

六 拒绝策略 默认使用第一种
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务 ThreadPoolExecutor.CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务

七线程池的体系图谱
在这里插入图片描述
八.线程池分解

** 1.ThreadPoolExecutor (普通)线程池两种执行方法 两者底层都是掉了同一个方法 execute()**
excuete()
submit() 有返回值 多了实现RunnableFuture 接口
submit 自身传入一个是 futuretask, excuete() 就是普通的一个task

源码分析

在这里插入图片描述

在这里插入图片描述

4个步骤进行:

  • (1).如果少于corePoolSize线程运行,
  • (2).如果大于corePoolSize线程运行,加入队列当中
  • (3).前两个都不满足 就添加一个新线程,
  • (4)如果3加入不了 addwork 工作方法里就执行handler拒绝策略,

execute(){
if (workerCountOf© < corePoolSize) { //1
if (addWorker(command, true);
}
if (isRunning© && workQueue.offer(command)) { //2
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0) //3
addWorker(null, false);
}
else if (!addWorker(command, false)) //4
reject(command);
}

addWorker(){
//自旋
1.根据状态 只在必要时检查队列是否为空
2. 自旋检查核心数量 检验配置参数
3. Worker w = new Worker(firstTask); 加入线程 执行run 方法
4. workers.add(w) 根据状态统计线程数量 (workers 非线程安全的 所以代码中用到了 ReentrantLock 锁)
5. workerAdded =true 如果可以加入work
6. t.start(); 那就会调用 这个 t 是线程本身 执行线程
}

Worker extends AbstractQueuedSynchronizer implements Runnable // 他实现Runable 接口 必定会执行 run方法
run(){
runWorker(){
(task != null || (task = getTask()) != null) // 会去调用
1. getTask(){ //去队列里面取值 在四个步骤第二个的时候 塞值workQueue.offer(command)
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
}
2. 根据状态会去校验是否需要关闭
3.task.run //这里就是我们自己重写的run 方法
}
4.最终还会调用一个processWorkerExit (){
(1)判断时候需要一处当前线程 还是要继续 addWorke();
}
}

流程图
在这里插入图片描述
在这里插入图片描述2. 定时线程池 ScheduledThreadPoolExecutor

schedule() //定时任务
scheduleAtFixedRate //固定速率连续执行 :必须等上次任务执行完才会执行下个 (源码中 传入的时间是正数数)
scheduleWithFixedDelay// 非固定速率连续执行:延时时间内任务没执行完,任务会启动多个并行执行 (源码中 传入的时间是负数)
ScheduledThreadPoolExecutor.DelayedWorkQueu//延迟队列

schedule(){
RunnableScheduledFuture<?> t = decorateTask()//返回值
delayedExecute(t){
if (isShutdown())
reject(task); //线程池关闭的话就拒绝
else
super.getQueue().add(task); //往延迟队列去塞任务 (会造成阻塞 因为往队列塞值会上锁)
if (isShutdown() &&
!canRunInCurrentRunState(task.isPeriodic()) &&
remove(task))
task.cancel(false);
else「
ensurePrestart();{ //判断核心线程池大小
addWorker(null, true);
}
}

}
}

ScheduledThreadPoolExecutor .public void run() {
boolean periodic = isPeriodic(); //判断是否周期
if (!canRunInCurrentRunState(periodic))
cancel(false);
else if (!periodic)
ScheduledFutureTask.super.run();
else if (ScheduledFutureTask.super.runAndReset()) {
setNextRunTime(); //下次运行时间
reExecutePeriodic(outerTask);
}
}

重点
super.getQueue().add(task) //任务如何往延迟队列塞值{
java.util.concurrent.ScheduledThreadPoolExecutor.DelayedWorkQueue#add{

		      public boolean offer(Runnable x) { //涉及到二叉树排序算法
					         if (x == null)
					                throw new NullPointerException();
					            RunnableScheduledFuture<?> e = (RunnableScheduledFuture<?>)x;
					            final ReentrantLock lock = this.lock;
					            lock.lock();
					            try {
					                int i = size;
					                if (i >= queue.length) //判断是否扩容
					                    grow();
					                size = i + 1;
					                if (i == 0) { //如果第一个 把task放进去  排序
					                    queue[0] = e;
					                    setIndex(e, 0);
					                } else {
					                    siftUp(i, e);
					                }
					                if (queue[0] == e) {
					                    leader = null;
					                    available.signal(); //如果是第一个就唤醒  使用条件锁 condition
					                }
					            } finally {
					                lock.unlock();
					            }
					            return true;
					        }

}

}

在这里插入图片描述

九.单列模式

饿汉模式 :线程安全
懒汉模式:非线程安全 但是如果在静态方法上面加锁(方法级别的锁) 是线程安全的 (同步锁 ,效率低)
如果在码块中加锁也是非线程安全(涉及到重排序的问题) 所有衍生了双重检查方式来解决重排问题
内部类的方式
枚举的方式

细节
1.jsp 查看java 进程 , jstack *** 查看线程状态可以排查线程死锁
2.join 保证线程的顺序 靠着native修饰的本地方法wait 方法 ,让主线去等待
3. 可以设置线程池监控

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值