这不坑爹呢 。
虽然对于非互联网公司貌似也够用,最起码简单省事发面快,咳咳,被小时候安琪酵母的广告洗脑了 。
那我们自己写吧,看了看上面工厂生产的前四个线程池,貌似都是用下面这个基础函数生成的:
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 池中最大线程数keepAliveTime 当线程数超过核心线程数时,这个代表空线程在被终止前最长生存时间unit 最长存活时间的单位workQueue 线程池的工作队列,这个队列仅保存被Runnable任务execute方法提交的任务threadFactory 执行程序创建新线程时要使用的工厂(一般用默认Executors.defaultThreadFactory()即可)workQueue 拒绝策略,表示当线程队列满了并且工作线程大于等于线程池的最大显示数(maxnumPoolSize)时如何来拒绝.拒绝策略又有啥呢?
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常 。ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常 。ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务ThreadPoolExecutor.CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务最初直观感受就是不能丢消息,用CallerRunsPolicy吧,主线程(暂且这么说吧,准确说是启动线程池的线程)跟着做业务 。然后就发现,主线程一旦运行任务,即使线程池里的线程跑完任务都不会再进任务,因为主线程被占住了,直到主线程跑完一次业务,才能继续分配给线程池任务 。问题很明显,业务流程比较耗时,线程池的一旦干完活,啥都干不了,都等着主线程消费队列的数据给新任务 。其他三个策略,AbortPolicy直接抛异常,抛了能咋样,还是不知道要干啥;DiscardOldestPolicy丢弃老任务,丢消息,否了;DiscardPolicy丢弃,肯定否了 。
我们要不然自己实现试试?
自己创建线程池那我们尝试创建一个自己的线程池:
int processors = Runtime.getRuntime().availableProcessors(); int corePoolSize = processors+1; int maximumPoolSize = corePoolSize*2; ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(corePoolSize,maximumPoolSize,5L,TimeUnit.MINUTES,new LinkedBlockingDeque<Runnable>(3),Executors.defaultThreadFactory(),(r, e) -> {if (!e.isShutdown()) {r.run();} else {LoggerFactory.getLogger(ThreadPoolTest.class).error("Task " + r.toString() + " rejected from " + e.toString());}} );拒绝策略我简单写了点,在具体情况下是可以根据需要自己实现 。关于Java中Thread类的线程的小知识我们在Thread类中,有一个State枚举类,为什么只有runnable 而没有running状态呢?
public enum State {NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TERMINATED;}其实,这是因为现在的操作系统架构通常都是用所谓的“时间分片(time quantum or time slice)”方式进行抢占式(preemptive)轮转调度(round-robin式) 。更复杂的可能还会加入优先级(priority)的机制 。这个时间分片通常是很小的,一个线程一次最多只能在 cpu 上运行比如10-20ms 的时间(此时处于 running 状态),时间片用后就要进行状态保存然后被切换下来放入调度队列的末尾等待再次调度(也即回到 ready 状态) 。如果期间进行了 I/O 的操作还会导致提前释放时间分片,并进入等待队列;或者是时间分片没有用完就被抢占 。
不计切换开销(每次在1ms 以内)的话,相当于1秒内有50-100次切换 。事实上时间片经常没用完,线程就因为各种原因被中断,实际发生的切换次数还会更多 。
时间分片也是可配置的,如果不追求在多个线程间很快的响应,也可以把这个时间配置得大一点,以减少切换带来的开销 。
通常,Java的线程状态是服务于监控的,所以 Java 线程把调度委托给了操作系统,我们在虚拟机层面看到的状态实质是对底层状态的映射及包装 。cpu 线程切换这么快,区分 ready 与 running 也没什么意义 。因此,统一成为runnable 状态是不错的选择 。
推荐阅读
- 狐猴的种类 狐猴是什么动物
- 这是一个美丽的村庄 奇怪的村庄
- 泾阳茯砖茶能保存多久,茯砖茶属于什么茶
- 老竹大方茶有什么特点,老竹大方产地风光介绍
- 结石喝什么茶,喝什么茶好睡眠
- 喝姜枣茶有什么好处呢,每天喝红枣茶有什么好处
- 金骏眉茶的泡法,什么茶叶最好喝
- 红茶水洗脸有什么好处,用茶水敷脸有什么好处和坏处
- 喝什么茶止咳化痰,有什么茶止咳
- 4个Excel技巧,财务人员快速判断,2个部门采购价格是否相同
