文章目录
线程池:https://www.bilibili.com/video/BV1YN411o7Lz?from=search&seid=7073118730878150075
一种自增例子i++
这个使用List实现的,查看List中元素个数:
线程方式
线程池方式
三种线程池
CorePoolSize表示核心线程数
MaximumPoolSize:表示队列满的情况下,最大的线程数
非核心线程数等于Max-Core
Cahced核心线程为0,非核心线程为2的32次方-1个,队列长度为1,只要一个任务放入队列,必须马上取走.
Single核心线程为1,非核心线程为为0,队列长度为2的32次方-1
Fixed核心线程数为n=10,非核心线程数为0,队列长度为2的32次方-1
阿里巴巴为什么不推荐这三种线程池
Cached:最高 CPU 100%,如果有N个任务,并且任务执行时间比较长,他的队列又只能存放一个任务,那么就会对应着创建N个线程,并且CachedThreadPool允许创建2的32次方-1个线程,就会创建非常多的线程,导致CPU 100%运行.
Fixed Single:内存 OOM,当任务过多,就会放到LinkedBlockingQueue中,放不下的时候,会造成内存溢出
自定义一个线程池:
在31个任务的时候,出现了RejectExecutionException
提交优先级和执行优先级
maximumPoolSize : 当队列中存放的任务达到队列容量的时候,当前可以同时运行的线程数量变为最大线程数。
提交优先级:核心线程 队列 非核心线程
执行优先级:核心线程 非核心线程 队列
为什么这么设计?
提交优先级
ThreadPoolExecutor类
execute(Runnable command){
//有三步:小于核心数,进入核心线程
//大于核心线程数,进入队列
//大于核心线程数且队列已满,则进入非核心线程
//如果都已经满了,则进入拒绝策略.
}
拒绝策略:
默认:RejectExecutionException拒绝执行策略
4.3 执行execute()方法和submit()方法的区别是什么呢?
execute()方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功与否;
submit()方法用于提交需要返回值的任务。线程池会返回一个 Future 类型的对象,通过这个 Future 对象可以判断任务是否执行成功,并且可以通过 Future 的 get()方法来获取返回值,get()方法会阻塞当前线程直到任务完成,而使用 get(long timeout,TimeUnit unit)方法则会阻塞当前线程一段时间后立即返回,这时候有可能任务没有执行完。
- 异步计算的结果(Future)
Future 接口以及 Future 接口的实现类 FutureTask 类都可以代表异步计算的结果。
当我们把 Runnable接口 或 Callable 接口 的实现类提交给 ThreadPoolExecutor 或 ScheduledThreadPoolExecutor 执行。(调用 submit() 方法时会返回一个 FutureTask 对象)
4.4 加餐:Callable+ThreadPoolExecutor+submit()+execute()方法示例代码
https://gitee.com/SnailClimb/JavaGuide/blob/master/docs/java/multi-thread/java%E7%BA%BF%E7%A8%8B%E6%B1%A0%E5%AD%A6%E4%B9%A0%E6%80%BB%E7%BB%93.md
ExecutorService 中没有execute()方法
public interface ExecutorService extends Executor {
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
}
Executor只有一个方法execute,这是Executor的全部内容
public interface Executor {
/**
* Executes the given command at some time in the future. The command
* may execute in a new thread, in a pooled thread, or in the calling
* thread, at the discretion of the {@code Executor} implementation.
*
* @param command the runnable task
* @throws RejectedExecutionException if this task cannot be
* accepted for execution
* @throws NullPointerException if command is null
*/
void execute(Runnable command);
}
ublic class ThreadPool01 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executor = new ThreadPoolExecutor(5,10,100, TimeUnit.SECONDS,new ArrayBlockingQueue<>(100),new ThreadPoolExecutor.AbortPolicy());
List<Future<Integer>> futures = new ArrayList<>();
for(int i=0;i<10;i++){
// FutureTask<Integer> futureTask = new FutureTask<Integer>(new Callable<Integer>() {
// @Override
// public Integer call() throws Exception {
// System.out.println(Thread.currentThread().getName()+"执行了");
// return 300;
// }
// });
//executor.execute(futureTask);//execute方法没有返回值,execute(Runnable command);
//提交任务到线程池
//这里不可以写FutureTask,只能Callable
Future<Integer> submit = executor.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName() + "执行了");
return 300;
}
});//
futures.add(submit);
}
for(Future<Integer> future:futures){
System.out.println(future.get());
}
//终止线程池
executor.shutdown();
while(!executor.isTerminated()){}
System.out.println("main come over");
}
}
七 线程池大小确定
有一个简单并且适用面比较广的公式:
CPU 密集型任务(N+1): 这种任务消耗的主要是 CPU 资源,可以将线程数设置为 N(CPU 核心数)+1,比 CPU 核心数多出来的一个线程是为了防止线程偶发的缺页中断,或者其它原因导致的任务暂停而带来的影响。一旦任务暂停,CPU 就会处于空闲状态,而在这种情况下多出来的一个线程就可以充分利用 CPU 的空闲时间。
I/O 密集型任务(2N): 这种任务应用起来,系统会用大部分的时间来处理 I/O 交互,而线程在处理 I/O 的时间段内不会占用 CPU 来处理,这时就可以将 CPU 交出给其它线程使用。因此在 I/O 密集型任务的应用中,我们可以多配置一些线程,具体的计算方法是 2N。
如何判断是 CPU 密集任务还是 IO 密集任务?
CPU 密集型简单理解就是利用 CPU 计算能力的任务比如你在内存中对大量数据进行排序。单凡涉及到网络读取,文件读取这类都是 IO 密集型,这类任务的特点是 CPU 计算耗费时间相比于等待 IO 操作完成的时间来说很少,大部分时间都花在了等待 IO 操作完成上。