一、线程池
下面这几组大家应该很属于,加 s 的都是工具类
Array Arrays
Collection Collections
Executor Executors
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
//上面和下面是等价的
List<String> list = Arrays.asList("a","b","c");
1.获取线程池的方法
1.1三种获取线程池的方法(但是,这三种在生产中都不允许使用!!!)
1.一池固定数线程
2.一池一线程
3.一池多线程
1. ExecutorService executorService1 = Executors.newFixedThreadPool(5);
2. ExecutorService executorService2 = Executors.newSingleThreadExecutor();
3. ExecutorService executorService3 = Executors.newCachedThreadPool();
底层源码如下:
所有线程池底层都是调这个构造方法实现的
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
下面给大家分享一下七大参数的详解
corePoolSize:线程池中常驻核心线程数(比如:银行网点,每天默认只开两个窗口给客服办理业务)
maximumPoolSize:线程池能够同时容纳最大的线程数,必须大于等于1(比如:银行网点,最多只有五个窗口给客户办理业务)
keepAliveTime:多余线程的存活时间(比如:银行网点,每天默认只开两个窗口给客服办理业务,今天中午人很多,又临时开了三个窗口给客户办理业务,下午人又少了,60分钟内,没有人办理业务,临时窗口就关闭,一般个unit参数一起使用,一个控制数字,一个控制单位,组合控制时间长短)
unit:keepAliveTime的单位(解释在上面)
workQueue:任务队列,被提交但尚未执行(比如:银行网点的候客区)
threadFactory:表示生成线程池中工作线程的工厂,一般默认即可(比如:银行网点的HR,负责找员工过来上班)
handler:拒绝策略,表示队列满了,并且工作线程数等于线程池最大线程数了。(比如:银行网点,候客区人满了,没有座位了,并且五个窗口都已经开了,此时银行保安就会拒绝其他客户来办理业务)
2.JDK内置的拒绝策略
AbortPolicy(默认):直接抛出RejectedExecutionException异常,阻止系统正常执行
CallerRunsPolicy:“调用者运行”一种调用机制,该策略既不会抛出异常,也不会抛弃任务,而是将默些任务回退到调用者,从而降低新的任务流量
DiscardOldestPolicy:丢弃队列中等待最久的任务,然后把当前任务加入到队列中尝试重新提交任务
DiscardPolicy:直接丢任务,不抛任何异常。如果允许任务丢失,这是最好的策略。
3. 生产情况下,自己写线程池
先说说为什么我们不用内置的线程池
我们都知道创建线程需要一个阻塞队列,源码用的是LinkedBlockingQueue,我们又知道LinkedBlockingQueue是一个可以说是无界的队列,因为他的长度是Integer的最大值(2147483647),所有可以说他是一个无界的
3.1 手写线程池
下面我们开始写一个自己的线程池:
/*
* Copyright (C), 2013-2019, 天津大海云科技有限公司
*/
import java.util.concurrent.*;
/**
* 我的线程池
*
* @author yangjikang
* @date 2019/6/19 10:39
* @modified By yangjikang
*/
public class TestExecutor {
public static void main(String[] args) {
ExecutorService executorService = new ThreadPoolExecutor(
2,
5,
1L,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
try {
for (int i = 0; i < 8; i++) {
final String tempUser = i + "号客户";
executorService.execute(() -> {
System.out.println(Thread.currentThread().getName() + "\t 窗口给" + tempUser + "办理业务...");
});
}
} catch (Exception e) {
e.printStackTrace();
}finally {
executorService.shutdown();
}
}
}
控制台效果图
上面,这段代码,我们是模拟8个客户来银行办理业务的场景。没有异常,属于正常情况
3.2 AbortPolicy拒绝策略
继续看下面的代码:
/*
* Copyright (C), 2013-2019, 天津大海云科技有限公司
*/
import java.util.concurrent.*;
/**
* 获取线程池
*
* @author yangjikang
* @date 2019/6/19 10:39
* @modified By yangjikang
*/
public class TestExecutor {
public static void main(String[] args) {
ExecutorService executorService = new ThreadPoolExecutor(
2,
5,
1L,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
try {
for (int i = 0; i < 10; i++) {
final String tempUser = i + "号客户";
executorService.execute(() -> {
System.out.println(Thread.currentThread().getName() + "\t 窗口给" + tempUser + "办理业务...");
});
}
} catch (Exception e) {
e.printStackTrace();
}finally {
executorService.shutdown();
}
}
}
现在我模拟是个用户来办理业务就抛出了异常,为什么呢?以为我们自己写得线程池用的拒绝策略是AbortPolicy
最多只支持最大线程数(代码中是5)加上队列最大界限(代码中是3)的线程数(结果就是8),所以十个用户过来就会抛出RejectedExecutionException异常。
3.3 CallerRunsPolicy拒绝策略
继续看下面的代码:
/*
* Copyright (C), 2013-2019, 天津大海云科技有限公司
*/
import java.util.concurrent.*;
/**
* 获取线程池
*
* @author yangjikang
* @date 2019/6/19 10:39
* @modified By yangjikang
*/
public class TestExecutor {
public static void main(String[] args) {
ExecutorService executorService = new ThreadPoolExecutor(
2,
5,
1L,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy());
try {
for (int i = 0; i < 10; i++) {
final String tempUser = i + "号客户";
executorService.execute(() -> {
System.out.println(Thread.currentThread().getName() + "\t 窗口给" + tempUser + "办理业务...");
});
}
} catch (Exception e) {
e.printStackTrace();
}finally {
executorService.shutdown();
}
}
}
为什么会有main线程呢?因为我们用的是CallerRunsPolicy拒绝策略
看一下我们上面介绍的含义,就是谁调用找谁处理,比如:A点的银行网点让你去B点的银行网点办理业务,B银行人满了,跟你说,A让你来的,你去找A办理业务
3.4 DiscardOldestPolicy拒绝策略
/*
* Copyright (C), 2013-2019, 天津大海云科技有限公司
*/
import java.util.concurrent.*;
/**
* 获取线程池
*
* @author yangjikang
* @date 2019/6/19 10:39
* @modified By yangjikang
*/
public class TestExecutor {
public static void main(String[] args) {
ExecutorService executorService = new ThreadPoolExecutor(
2,
5,
1L,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardOldestPolicy());
try {
for (int i = 0; i < 10; i++) {
final String tempUser = i + "号客户";
executorService.execute(() -> {
System.out.println(Thread.currentThread().getName() + "\t 窗口给" + tempUser + "办理业务...");
});
}
} catch (Exception e) {
e.printStackTrace();
}finally {
executorService.shutdown();
}
}
}
2号和3号客户没有办理业务(等待最久的不处理)
3.5 DiscardOldestPolicy拒绝策略
/*
* Copyright (C), 2013-2019, 天津大海云科技有限公司
*/
import java.util.concurrent.*;
/**
* 获取线程池
*
* @author yangjikang
* @date 2019/6/19 10:39
* @modified By yangjikang
*/
public class TestExecutor {
public static void main(String[] args) {
ExecutorService executorService = new ThreadPoolExecutor(
2,
5,
1L,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardPolicy());
try {
for (int i = 0; i < 10; i++) {
final String tempUser = i + "号客户";
executorService.execute(() -> {
System.out.println(Thread.currentThread().getName() + "\t 窗口给" + tempUser + "办理业务...");
});
}
} catch (Exception e) {
e.printStackTrace();
}finally {
executorService.shutdown();
}
}
}
8号和9号没有处理,直接抛弃,因为改线程池最多只能处理8个线程
4. 如何合理的分配线程数
4.1 CPU密集型(运算)
4.2 IO密集型(需要去数据库,redis等拿数据)
第一种
第二种