JUC学习之同步队列和线程池
1.同步队列
synchronousQueue和其他队列区别在于其不存储元素,put一个元素,必须take取出上一个才能再put进去,synchronousQueue只能有一个元素;
2.线程池
2.1 池化技术
准备一些资源,有人使用就从这里拿取,等到用完了后,再换回来。
2.2 优点
2.2.1 降低资源的消耗
因为创建和销毁非常消耗资源。
2.2.2 提高响应速度
不用一直新建销毁,节省了步骤。
2.2.3 方便管理
线程可以复用,可以控制最大并发数,可以管理线程。
3.线程池的具体了解
3.1 线程池的三大方法
3.1.1 创建单个线程的线程池
通过Executors工具类调用newSingleThreadExecutor()方法,创建只有一个固定的线程的线程池,下图为执行代码及结果:
public static void main(String[] args) {
ExecutorService threadPool = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+" ok");
});
}
}
3.1.2 创建固定个数的线程池
通过Executors工具类调用newFixedThreadPool(创建的固定线程个数)方法,创建一个固定个数的线程的线程池,下图为执行创建有5个线程的线程池的代码及结果:
ExecutorService threadPool = Executors.newFixedThreadPool(5);
try {
for (int i = 0; i < 10; i++) {
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+" ok");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPool.shutdown();
}
3.1.3 创建根据性能可伸缩个数的线程池
通过Executors工具类调用newCachedThreadPool()方法,创建一个个数根据硬件性能可伸缩线程个数的线程池,下图为执行创建可伸缩个数的线程池的代码及结果:
ExecutorService threadPool = Executors.newCachedThreadPool();
try {
for (int i = 0; i < 100; i++) {
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+" ok");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPool.shutdown();
}
3.2 线程池的七大参数
3.2.1三大线程池创建方法源码解析
3.2.1.1 单个线程线程池
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory));
}
3.2.1.2 固定个数线程线程池
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
3.2.1.3 根据性能可伸缩线程个数线程池
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
3.2.1.4 ThreadPoolExecutor创建线程池
通过上面三大方法源码我们可以知道,三大方法的本质是通过ThreadPoolExecutor创建的线程池,只是传递的参数不同,下面我们详解下具体的参数:
3.2.1.4.1 int corePoolSize是核心线程的个数,只要创建线程池核心线程就会一直存在;
3.2.1.4.2 int maximumPoolSize是最大线程的个数,创建线程池后最大可以有多少线程存在;
3.2.1.4.3 long keepAliveTime是超时等待的时间(等候区闲置线程的存货时间),当队列(等候区)中闲置的时间,超过这个时间就会关闭除了核心线程之外的闲置线程;
3.2.1.4.4 TimeUnit unit是超时等待时间的单位;
3.2.1.4.5 BlockingQueue<Runnable> workQueue)是设置等候区的阻塞队列(即最大线程数-核心线程数,可以不指定);
3.2.1.4.6 ThreadFactory threadFactory是创建线程的线程工厂;
3.2.1.4.7 RejectedExecutionHandler handler是拒绝策略,即当设置的最大线程数时所采取的策略;
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
注1: 推荐使用ThreadPoolExecutor创建线程池,因为newFixedThreadPool和newCachedThreadPool方法最大线程是是允许达到Integer.MAX_VALUE(21亿)的,可能会堆积大量的请求或者创建大量的线程,会导致OOM。
注2:线程池创建结束后应手动关闭,一般放在finally之中。
threadPool.shutdown();
3.3 四种拒绝策略
3.3.1 AbortPolicy()在超过最大线程数的时候,不在处理新的线程,会抛异常java.util.concurrent.RejectedExecutionException
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2,
3,
3,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
try {
for (int i = 1; i <= 10; i++) {
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+" ok");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPool.shutdown();
}
3.3.2 CallerRunsPolicy()策略是当线程达到最大的是时候,会将新增的线程扔给创建他的地方处理(下图为main方法中创建),不会抛出异常;
3.3.3 CallerRunsPolicy()策略,在达到最大线程数后,会丢弃新创建的线程,不抛异常;
3.3.4 DiscardOldestPolicy()策略,是在达到最大线程数后,尝试与最早的线程竞争(备胎的既视感。。。),如果竞争失败就被丢弃,不抛出异常。
- 点赞
- 收藏
- 关注作者
评论(0)