提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
JAVA线程
一、线程是什么?
线程是操作系统中CPU调度和分配的基本单位,也是能独立运行的最小单位。
二、怎么创建线程
1.继承Thread
public class MyThread extends Thread{
@Override
public void run(){
//这里写业务逻辑
}
}
//调用
MyThread myThread = new MyThread();
myThread.start();
2.实现Runnable接口
public class MyRunnable implements Runnable{
@Override
public void run() {
//这里写业务逻辑
}
}
//调用
MyRunnable m = new MyRunnable();
Thread t = new Thread(m);
t.start();
3.Callable接口
public class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
//这里写业务逻辑
return null;
}
}
可以看到实现Callable接口和实现Runnable接口的区别:
- Callable接口可以提供一个返回值,Runnable不行
- Callable接口的call方法抛出一个异常,Runnable没有
下面看一个Callable线程使用并获取返回值的简单例子
public class MyCallableDemo {
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
Callable<String> callable = ()->{
System.err.println("线程开始......");
Thread.sleep(5000);
System.err.println("线程结束......");
return "拜拜";
};
FutureTask<String> futureTask = new FutureTask<>(callable);
Thread thread = new Thread(futureTask);
thread.start();
String result = futureTask.get();
long time = (System.currentTimeMillis() - start)/1000;
System.err.println("线程已结束,返回值:" + result + ",用时:" + time);
}
}
不过实际工作中很少看到有人直接实现这个接口来处理业务,比较多的还是使用CompletableFuture来异步执行多个任务。
3.线程的生命周期
线程的生命周期通常包含五个状态——新建、就绪、运行、堵塞、死亡
**新建状态:**线程对象被创建,还未启动
**就绪状态:**线程已准备好运行,等待cpu调度
**运行状态:**线程正在执行
**堵塞状态:**线程因等待资源(I/O操作、其他线程的锁)或执行了某些方法(sleep)而暂停运行
**死亡状态:**线程完成了任务或因异常终止
运行->堵塞->就绪->运行,线程可以在这三种状态中切换,一旦进入死亡状态,线程就彻底结束,无法回到其他状态。
三、线程池
1.什么是线程池
线程池是一种多线程处理方式,将任务添加到队列中,然后创建线程自动启动线程执行。
2.线程池的创建
线程池的创建大体上可以分成两类,一种是通过Executors框架创建,一种是通过ThreadPoolExecutor类来创建。
通过Executors框架提供的静态方法创建线程池,这是常见的方式。包括以下几种:
- Executors.newFixedThreadPool(int nThreads):创建一个固定大小的线程池,任务队列满了,新任务会等待直到有线程空闲。
- Executors.newCachedThreadPool():创建一个可缓存的线程池,根据需要创建线程,如果线程空闲超过一定时间会自动回收
- Executors.newSingleThreadExecutor():创建一个单线程的线程池,所有任务按照先进先出的顺序执行
- Executors.newScheduledThreadPool(int corePoolSize):创建一个可以执行定时任务的线程池
- Executors.newWorkStealingPool():JDK 1.8添加,创建一个抢占式执行的线程池,任务执行顺序不确定
3.ThreadPoolExecutor
public ThreadPoolExecutor(int corePoolSize,//核心线程数
int maximumPoolSize,//最大线程数
long keepAliveTime,//线程空闲时间
TimeUnit unit,//时间单位
BlockingQueue<Runnable> workQueue,//任务队列
ThreadFactory threadFactory,//线程工厂
RejectedExecutionHandler handler//拒绝策略)
{
...
}
核心线程数:线程池中常驻的线程数量
最大线程数:线程池能容纳的最大线程数量
线程空闲时间:超出核心线程数的多余空闲线程在存活时间达到线程空闲时间后会自动销毁
时间单位:线程空闲时间的单位
任务队列:也叫等待队列,存放提交了但未执行的任务
线程工厂:线程池工作线程的创建工厂,用于创建线程
拒绝策略:当等待队列满了,并且线程数达到最大线程数时,对后续提交过来的任务的拒绝方式
4.等待队列
线程池的等待队列有以下类型:
- synchronousQueue:这是一个容量为0的等待队列,意思是每次提交任务时都必须要有一个删除操作,不然会抛出异常
- LinkedBlockingQueue:这是一个无界堵塞队列,意思是只要提交任务就塞入等待队列中去,直到有空闲线程去处理
- ArrayBlockingQueue:这是一个有界堵塞队列,可以直到队列的容量
5.拒绝策略
1.中止策略:当线程已达到最大线程数,且队列已满时,后续提交任务就会报异常,中止运行
2.调用者运行策略:把任务给到当前调用者线程去处理
3.丢弃策略:直接丢弃任务,不抛出异常
4.弃老策略:把队列最老的任务丢弃,新任务插入队列中