一、线程状态
省略
二、实现方式
2.1继承Thread
public class MyThread extends Thread{
public MyThread(String name){
this.setName(name);
}
@Override
public void run() {
System.out.println("该方法用于自定义线程执行的操作");
}
public static void main(String[] args) {
new MyThread("mine").start();
}
}
2.2实现Runnable
public class MyRunnable implements Runnable{
@Override
public void run() {
}
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
new Thread(myRunnable,"myRunnable").start();
}
}
2.3实现Callable,并使用FutureTask包装
public class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
return 123;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCallable myCallable = new MyCallable();
FutureTask<Integer> ft = new FutureTask<>(myCallable);
Thread thread = new Thread(ft);
thread.start();
System.out.println(ft.get());
}
}
三、Runnable、Callable、Future接口
类图关系如下
我们知道,构建Thread可以通过传入Runnable,但是通过观察类图,我们可以发现Callable接口并没有实现Runable,所以需要借助FutureTask进行包装才能使用。FutureTask实现了RunnableFuture接口,顾名思义,既实现了Runnable,又实现了Future。
三、线程池
3.1线程池的使用
Java提供了ThreadPoolExecutor类,要想构建一个线程池,一共提供了四个构造方法,涉及七个核心参数。
public class ExecutorsDemo {
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
for (int i = 0; i < 10; i++) {
threadPoolExecutor.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "hello");
}
});
}
}
}
3.2线程池的核心参数
-
核心线程数:默认一直存在于线程池中,而非核心线程数量长时间闲置会销毁
-
最大线程数:核心线程数+非核心线程数量
-
存活时间:非核心线程闲置超时时长
-
存活时间的单位
-
阻塞队列:维护着等待执行的Runnable任务对象,常用阻塞队列如下
- LinkedBlockingQueue:基于链表的阻塞队列,默认大小是Integer.MAX_VALUE
- ArrayBlockingQueue:基于数组的阻塞队列,需要指定大小
- SynchronousQueue:内部容量为0,每个put操作必须等待一个take操作
- DelayQueue:延迟队列,该队列中的元素只有当其指定的延迟时间到,才能够从队列中获取到该元素
-
线程工厂
-
拒绝策略:线程数量大于最大线程数时的处理策略,总共有四种
- ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出异常
- ThreadPoolExecutor.DiscardPolicy:丢弃新来的任务,不抛出异常
- ThreadPoolExecutor.DiscardOldestPolicy:抛弃队列头部的任务,重试
- ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
3.3线程池的工作流程
通过上面的核心参数,我们大概也能猜到线程池的工作流程,其实很简单,就是两个阈值与一个队列。
- 当线程数小于corePoolSize时,创建新线程
- 当线程数大于>=corePoolSize,后续的线程任务会自动加入阻塞队列
- 当阻塞队列满了,就会创建非核心线程
- 当阻塞队列满了,并且线程数达到了maximumPoolSize,就会执行拒绝策略
3.4常见线程池
Java提供了Executors类,它有4个静态方法,方便我们平时构建线程池。
- newCachedThreadPool
- newFixedThreadPool
- newSingleThreadPool
- newScheduledThreadPool
其实这些静态方法就是根据设置核心参数的不同值,以达到不同的效果,这边大家有兴趣的话请自行查看代码,不再赘述。
3.5类图关系