RejectedExecutionHandler handler
拒绝方式/拒绝策略(线程池考察的重点)
我们知道,线程池有一个阻塞队列,当阻塞队列满了之后,继续添加任务,我们该如何去应对?
Java系统的四个处理方式
ThreadPoolExecutor.AbortPolicy
直接抛出异常,这样做的话,整个线程池就不干活了
ThreadPoolExecutor.CallerRunsPolicy
谁是添加这个新任务的线程,就谁去执行这个任务(注意与第四个任务进行区分)
ThreadPoolExecutor.DiscardOldestPolicy
丢弃最早的任务,执行新的任务
ThreadPoolExcutor.DiscardPolicy
丢弃新的任务(就没有线程去执行这个任务了)
线程池的自行实现
class MyThreadPool{
//创建一个阻塞队列
BlockingQueue<Runnable> queue=new LinkedBlockingQueue<>();
//首先,线程池么,可肯定要有一个添任务的方法
public void submit(Runnable runnable) throws InterruptedException {
//其次,我们需要将新的任务添加到一个阻塞队列中去
queue.put(runnable);
}
//然后我们需要有方法去创建线程(固定数量的)
public MyThreadPool(int n){//n是指定有多少个线程
for (int i = 0; i < n; i++) {
Thread t=new Thread(()->{
while(true){
try {
//取出要执行的任务
Runnable runnable=queue.take();
runnable.run();//执行该任务
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
}
}
}
//自行实现线程池
public class Test {
public static void main(String[] args) throws InterruptedException {
MyThreadPool myThreadPool=new MyThreadPool(4);
for (int i = 0; i < 100; i++) {
myThreadPool.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"hello");
//Thread.currentThread()获取线程实例
}
});
}
}
}
运行结果如下由于此时线程的调度是随机的,当前给这个线程池中插入的任务,在执行的时候,也不一定是N个线程的工作完全均等,但是从统计意义上说,任务是均等的,另外,当前线程做的任务就只是一个简单的打印,花的时间短,很有可能你刚给A线程分配好任务,但是A线程一下就执行完了,那么下一次就很有可能还是A线程分配并执行同样的任务.
那么问题来了,创建线程池的时候,线程的个数是咋来的?
线程池的线程数目,网络上有很多说法,比如,假设CPU逻辑核心数为N,线程池线程个数:N,N+1,1.2*N,1.5*N,2*N....(但是这些都不准确!!!)不同的项目中,线程要做的工作,是不一样的
有的线程的工作是"CPU密集型",线程的工作全是运算在这种情况下,CPU大部分工作都是要在CPU上完成的.CPU得给他安排核心去完成工作才可以有进展,如果CPU是N个核心,当你线程数量也是N的时候,理想情况,每个核心上一个线程,如果搞很多的线程,线程也就是在排队等待,不会有新的进展.有的线程的工作是"IO密集型",读写文件,等待用户输入,网络通信等
这类型的操作涉及到大量的等待时间,在等待的过程中,没有使用CPU,这样的线程就算更多一些也不会给CPU造成太大的负担,比如CPU是16个核心,写32个线程,由于是IO密集型的,这里的大部分线程都在等,都不消耗CPU,反而CPU的占用情况还很低
实际开发过程中,一个线程往往是一部分工作是CPU密集的,一部分工作是IO密集的,此时,一个线程,几成是在CPU上运行,几成是在等待IO,这个是说不好的,这里更好的做法,即使通过实验的方式,来找到合适的线程数,也就是对其进行性能测试,尝试不同的线程数目,尝试过程中,找到性能和系统资源开销比较均衡的数值.