什么是线程?
线程是操作系统运算调动的最小单位,一个线程指的是单一顺序的控制流,是逻辑概念
线程和进程的区别
线程是虚拟的,但是进程却真实存在的
进程的主要功能是维护程序在操作系统上的活动,包含文本区,数据区和栈
进程可以调度一个或者多个线程,且能够保存调度前后相关数据
操作系统线程的生命周期
New:新建线程初始化状态 Runnable: 调用start()后处于就绪状态,等待获取cpu资源 Running: 获取到cpu资源执行任务 Dead:运行完毕死亡状态 Blocked:阻塞状态,一般调用sleep(),wait()方法进入阻塞,需要被唤醒才能继续执行任务,处于阻塞状态的线程会被操作系统冻结当前状态,让出资源给OS调用
线程常用方法
sleep(5s): 线程休眠,休眠后处于阻塞状态,由操作系统负责到期唤醒
join(): 让一个线程t1加入到当前线程main的工作中,当前线程main执行到此处时会阻塞,然t1执行完毕后main才会唤醒继续执行,广泛用于多线程编程中
yeild(): 当前线程让出使用权
wait(): Java Object对象的方法,通常用于线程同步策略,调用后需要通过notify()/notifyAll()方法唤醒
notify()/notifyAll():释放锁资源,让阻塞中线程处于就绪状态
Java线程的实现方式
- extend Thread ,重写run()实现自己的逻辑
- implement Runnable,实现run(),启动时需要使用new Thread().start()
public class Task implements Runnable{
@Override
public void run() {
}
public static void main(String[] args) {
Thread t1 = new Thread(new Task());
t1.start();
}
}
3.实现Callable,这是一个线程执行回调的实现
public class Task implements Callable {
@Override
public Object call() throws Exception {
System.out.println("return需要回调的数据");
return null;
}
public static void main(String[] args) {
FutureTask futureTask=new FutureTask(new Task());
futureTask.run();
}
}
Java线程池的实现
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
- corePoolSize 核心线程数
- maximumPoolSize 最大线程数
- keepAliveTime 可回收线程(最大线程数-核心线程数)的存活时间
- TimeUnit 存活时间单位
- BlockingQueue 阻塞队列
- ThreadFactory 线程工厂
- RejectedExecutionHandler 拒绝策略
BlockingQueue :如果任务数超出线程池处理能力时加入队列常用的实现类有:
- ArrayBlockingQueue 有界队列,数组结构
- LinkedBlockingDeque 无解队列,双向链表结构
- SynchronousQueue 单元素队列
RejectedExecutionHandler 有四种
- CallerRunsPolicy 任务来时不用线程池线程执行,而是主线程
- AbortPolicy 丢弃任务且抛异常
- DiscardPolicy 丢弃任务不抛异常
- DiscardOldestPolicy 丢弃队列最前面的任务,然后重新尝试执行任务
- 实现RejectedExecutionHandler接口自定义处理逻辑
线程池核心流程代码:
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
//如果运行的线程少于corePoolSize尝试添加新线程
if (addWorker(command, true))
return;
c = ctl.get();
}
//到这里线程数大于等于核心线程数
//如果任务可以成功排队,那么我们仍然需要再次检查我们是否应该添加线程
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
//再次检查线程池状态,因为自上次检查以来已有的线程已死亡或线程池在进入此方法后关闭
//如果非运行中就回滚队列任务
if (! isRunning(recheck) && remove(command))
//执行拒绝策略
reject(command);
//运行中且运行的线程数为0就添加新线程
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
//如果无法对任务进行排队,则尝试添加新的新线程。如果添加失败了,我们知道线程池已经关闭或饱和因此拒绝该任务。
else if (!addWorker(command, false))
reject(command);
}
- 也有其他的一些线程池的实现不做介绍
- 线程池的核心线程数一般不会被空闲回收,但是也可以配置allowCoreThreadTimeOut