所谓的线程池的 7 大参数是指,在使用 ThreadPoolExecutor 创建线程池时所设置的 7 个参数,如以下源码所示:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
}
这 7 个参数分别是:
- corePoolSize:核心线程数。
- maximumPoolSize:最大线程数。
- keepAliveTime:空闲线程存活时间。
- TimeUnit:时间单位。
- BlockingQueue:线程池任务队列。
- ThreadFactory:创建线程的工厂。
- RejectedExecutionHandler:拒绝策略。
核心线程数:是指线程池中长期存活的线程数。
这就好比古代大户人家,会长期雇佣一些“长工”来给他们干活,这些人一般比较稳定,无论这一年的活多活少,这些人都不会被辞退,都是长期生活在大户人家的。
最大线程数:线程池允许创建的最大线程数量,当线程池的任务队列满了之后,可以创建的最大线程数。
这是古代大户人家最多可以雇佣的人数,比如某个节日或大户人家有人过寿时,因为活太多,仅靠“长工”是完不成任务,这时就会再招聘一些“短工”一起来干活,这个最大线程数就是“长工”+“短工”的总人数,也就是招聘的人数不能超过 maximumPoolSize。
注意事项
最大线程数 maximumPoolSize 的值不能小于核心线程数 corePoolSize,否则在程序运行时会报 IllegalArgumentException 非法参数异常,如下图所示:

空闲线程存活时间,当线程池中没有任务时,会销毁一些线程,销毁的线程数=maximumPoolSize(最大线程数)-corePoolSize(核心线程数)。
还是以大户人家为例,当大户人家比较忙的时候就会雇佣一些“短工”来干活,但等干完活之后,不忙了,就会将这些“短工”辞退掉,而 keepAliveTime 就是用来描述没活之后,短工可以在大户人家待的(最长)时间。
时间单位:空闲线程存活时间的描述单位,此参数是配合参数 3 使用的。参数 3 是一个 long 类型的值,比如参数 3 传递的是 1,那么这个 1 表示的是 1 天?还是 1 小时?还是 1 秒钟?是由参数 4 说了算的。TimeUnit 有以下 7 个值:
在Java中,BlockingQueue是一个接口,它的实现类有ArrayBlockingQueue、DelayQueue、 LinkedBlockingQueue、PriorityBlockingQueue、SynchronousQueue等,它们的区别主要体现在存储
结构上或对元素操作上的不同,但是对于take与put操作的原理,却是类似的,目的都是阻塞存取。
有界队列:就是有固定大小的队列。比如设定了固定大小的 LinkedBlockingQueue,又或者大小为 0,只是在生产者和消费者中做中转用的 SynchronousQueue。
无界队列:指的是没有设置固定大小的队列。这些队列的特点是可以直接入列,直到溢出。当然现实几乎不会有到这么大的容量(超过 Integer.MAX_VALUE),所以从使用者的体验上,就相当于 “无界”。比如没有设定固定大小的 LinkedBlockingQueue。
阻塞和非阻塞指的是调用者(程序)在等待返回结果(或输入)时的状态。阻塞时,在调用结果返回前,当前线程会被挂起,并在得到结果之后返回。非阻塞时,如果不能立刻得到结果,则该调用者不会阻塞当前线程。因此对应非阻塞的情况,调用者需要定时轮询查看处理状态。
add(E e):(非阻塞)调用offer但会根据offer结果,如果false抛出 IllegalStateException(“Queue full”)
offer(E e):(非阻塞)如果队列没满,立即返回true; 如果队列满了,立即返回false
put(E e):(阻塞)如果队列满了,一直阻塞,直到队列不满了或者线程被中断
offer(E e, long timeout, TimeUnit unit):在队尾插入一个元素,,如果队列已满,则进入等待,直到出现以下三种情况:
1.被唤醒
2.等待时间超时
3.当前线程被中断
poll():(非阻塞)如果没有元素,直接返回null;如果有元素,出队
remove():(非阻塞)删除队列头元素,如果没有元素,返回false
take():(阻塞)如果队列空了,一直阻塞,直到队列不为空或者线程被中断
poll(long timeout, TimeUnit unit):如果队列不空,出队;如果队列已空且已经超时,返回null;如果队列已空且时间未超时,则进入等待,直到出现以下三种情况:
1.被唤醒
2.等待时间超时
3.当前线程被中断
element(): 调用peek(),查看元素,拿到为null,抛出 NoSuchElementException。
peek():查看元素,不去除,如果拿不到则为null。
阻塞队列:是一种特殊的队列,它在普通队列的基础上提供了两个附加功能。

即:
在线程池中,阻塞队列是用来存储线程池的所有待执行任务的队列。它可以设置以下几个值:
- ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列。
特点:ArrayBlockingQueue底层是使用一个数组实现队列的,内部使用了一把锁对插入和取出做了限制,即插或者取的操作是原子性
容量:需要指定一个大小,创建了无法修改
元素:不允许为null的元素插入
- LinkedBlockingQueue:一个由链表结构组成的有界阻塞队列。
特点:内部有两把锁,即入队锁和出队锁(ReentrantLock+Condition),插入和取出各一把,互不打扰。两把锁来控制插入和取出数组阻塞唤醒。内部通过AtomicInteger count变量保证统计队列元素准确
容量:默认为Integer.MAX_VALUE
元素:不允许为null的元素插入
- SynchronousQueue:一个不存储元素的阻塞队列,即直接提交给线程不保持它们。
一种无缓冲的等待队列,相对于有缓冲的BlockingQueue来说,少了一个中间经销商的环节(缓冲区)。消费者必须亲自去集市找到所要商品的直接生产者
特点:一对一,生产者和消费者缺一就阻塞,存在公平和非公平两种
容量:size默认为0,剩余容量也为0
元素:不允许为null的元素插入
- PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列。
特点:无界队列依赖Comparator来确保不同元素的排序位置,最大值不超过Integer.MAX_Value-8
容量:默认大小为11,底层使用数组来存储,会扩容
元素:不允许为null的元素插入
- DelayQueue:一个使用优先级队列实现的无界阻塞队列,只有在延迟期满时才能从中提取元素。
特点:存储Delayed元素,可实现延时等功能
容量:默认为11,底层使用PriorityBlockingQueue来存储
元素:不允许为null的元素插入,内部存储Delay的实现类元素
take:内部使用priorityblockingqueue排序,根据getDelay判断剩余时间,只有当前到点了,才可以取出元素
- LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。
特点:BlockingDeque 类是一个双端队列,在不能够插入元素时,它将阻塞住试图插入元素的线程;在不能够抽取元素时,它将阻塞住试图抽取的线程。
容量:可以指定队列的容量(防止过度膨胀),如果不指定,默认容量大小等于Integer.MAX_VALUE。
元素:同时支持FIFO和FILO两种操作方式(即可以从队列的头和尾同时操作(插入/删除)),支持线程安全。
- LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。与SynchronousQueue类似,还含有非阻塞方法。
public interface TransferQueue<E> extends BlockingQueue<E> {
// 如果可能,立即将元素转移给等待的消费者。
// 如果存在消费者已经等待接收它(在 take 或 timed poll(long,TimeUnit)poll)中,则立即传送指定的元素,否则返回 false。
boolean tryTransfer(E e);
// 将元素转移给消费者,如果需要的话等待。
// 如果存在一个消费者已经等待接收它(在 take 或timed poll(long,TimeUnit)poll)中,则立即传送指定的元素,否则等待直到元素由消费者接收。
void transfer(E e) throws InterruptedException;
// 上面方法的基础上设置超时时间
boolean tryTransfer(E e, long timeout, TimeUnit unit) throws InterruptedException;
// 如果至少有一位消费者在等待,则返回 true
boolean hasWaitingConsumer();
// 返回等待消费者人数的估计值
int getWaitingConsumerCount();
}
特点:实现了TransferQueue接口。TransferQueue接口继承了BlockingQueue,主要扩展了两个方法tryTransfer、transfer。
对比:和SynchronousQueue.TransferQueue(公平模式)相比,它是可以统计长度,可以进行查询的;和LinkedBlockingQueue相比,它拥有更高的性能(使用CAS自旋);和ConcurrentLinkedQueue相比,它拥有阻塞功能。因此可以看作是ConcurrentLinkedQueue、SynchronousQueue、LinkedBlockingQueue的超集,作为对比学习。既然说到了,那就顺便说一下ConcurrentLinkedQueue吧。
总结:
ArrayBlockingQueue:需要创建队列数组长度。
LinkedBlockingQueue:内部使用Node实现,默认大小Integer.MAX_VALUE。
PriorityBlockingQueue:优先级队列,默认大小11,内部需实现Comparator来比较。
DelayQueue:延时队列,元素需要实现Delayed,底层使用PriorityBlockingQueue,默认大小11。
SynchronousQueue:交换队列,默认大小0,需同时存在生产者和消费者,否则任一都会阻塞
LinkedTransferQueue:新增transfer方法,tryTransfer和transfer可以检测是否有线程在等待获取数据,如果检测到就立即发送新增的数据给这个线程获取而不用放入队列。
-----------------------------------------------------------------------------------------
顺便提一下ConcurrentLinkedQueue。
- ConcurrentLinkedQueue:是一种非阻塞的无界的线程安全队列,与阻塞队列LinkedBlockingQueue相对应,ConcurrentLinkedQueue同样也是使用链表实现的FIFO队列,但不同的是它没有使用任何锁机制,而是用自旋+CAS来实现线程安全。
特点:
1.不允许null入列
2.在入队的最后一个元素的next为null
3.队列中所有未删除的节点的item都不能为null且都能从head节点遍历到
4.删除节点是将item设置为null, 队列迭代时跳过item为null节点
5.head节点跟tail不一定指向头节点或尾节点,可能存在滞后性
比较常用的是 LinkedBlockingQueue,线程池的排队策略和 BlockingQueue 息息相关。
线程工厂:线程池创建线程时调用的工厂方法,通过此方法可以设置线程的优先级、线程命名规则以及线程类型(用户线程还是守护线程)等。线程工厂的使用示例如下:
public static void main(String[] args) {
// 创建线程工厂
ThreadFactory threadFactory = new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
// 创建线程池中的线程
Thread thread = new Thread(r);
// 设置线程名称
thread.setName("Thread-" + r.hashCode());
// 设置线程优先级(最大值:10)
thread.setPriority(Thread.MAX_PRIORITY);
//......
return thread;
}
};
// 创建线程池
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 10, 0,TimeUnit.SECONDS, new LinkedBlockingQueue<>(),threadFactory); // 使用自定义的线程工厂
threadPoolExecutor.submit(new Runnable() {
@Override
public void run() {
Thread thread = Thread.currentThread();
System.out.println(String.format("线程:%s,线程优先级:%d",
thread.getName(), thread.getPriority()));
}
});
}
以上程序的执行结果如下:

从上述执行结果可以看出,自定义线程工厂起作用了,线程的名称和线程的优先级都是通过线程工厂设置的。
拒绝策略:当线程池的任务超出线程池队列可以存储的最大值之后,执行的策略。默认的拒绝策略有以下 4 种:
线程池的默认策略是 AbortPolicy 拒绝并抛出异常。
线程池中线程数小于corePoolSize时,新任务将创建一个新线程执行任务,不论此时线程池中是否存在空闲线程。
线程池中线程数达到corePoolSize时,新任务将被放入workQueue中,等待线程池中任务调度执行;
当workQueue已满,且maximumPoolSize>corePoolSize时,新任务会创建新线程执行任务;
当workQueue已满,且提交任务数超过maximumPoolSize,任务由RejectedExecutionHandler处理;
当线程池中线程数超过corePoolSize,且超过这部分的空闲时间达到keepAliveTime时,回收该线程;
如果设置allowCoreThreadTimeOut(true)时,线程池中corePoolSize范围内的线程空闲时间达到keepAliveTime也将回收;
本文介绍了线程池的 7 大参数:
exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby中使用两个参数异步运行exe吗?我已经尝试过ruby命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何rubygems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除
我有一些Ruby代码,如下所示:Something.createdo|x|x.foo=barend我想编写一个测试,它使用double代替block参数x,这样我就可以调用:x_double.should_receive(:foo).with("whatever").这可能吗? 最佳答案 specify'something'dox=doublex.should_receive(:foo=).with("whatever")Something.should_receive(:create).and_yield(x)#callthere
我正在为一个项目制作一个简单的shell,我希望像在Bash中一样解析参数字符串。foobar"helloworld"fooz应该变成:["foo","bar","helloworld","fooz"]等等。到目前为止,我一直在使用CSV::parse_line,将列分隔符设置为""和.compact输出。问题是我现在必须选择是要支持单引号还是双引号。CSV不支持超过一个分隔符。Python有一个名为shlex的模块:>>>shlex.split("Test'helloworld'foo")['Test','helloworld','foo']>>>shlex.split('Test"
我不确定传递给方法的对象的类型是否正确。我可能会将一个字符串传递给一个只能处理整数的函数。某种运行时保证怎么样?我看不到比以下更好的选择:defsomeFixNumMangler(input)raise"wrongtype:integerrequired"unlessinput.class==FixNumother_stuffend有更好的选择吗? 最佳答案 使用Kernel#Integer在使用之前转换输入的方法。当无法以任何合理的方式将输入转换为整数时,它将引发ArgumentError。defmy_method(number)
两者都可以defsetup(options={})options.reverse_merge:size=>25,:velocity=>10end和defsetup(options={}){:size=>25,:velocity=>10}.merge(options)end在方法的参数中分配默认值。问题是:哪个更好?您更愿意使用哪一个?在性能、代码可读性或其他方面有什么不同吗?编辑:我无意中添加了bang(!)...并不是要询问nobang方法与bang方法之间的区别 最佳答案 我倾向于使用reverse_merge方法:option
我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("
我有一个只接受一个参数的方法:defmy_method(number)end如果使用number调用方法,我该如何引发错误??通常,我如何定义方法参数的条件?比如我想在调用的时候报错:my_method(1) 最佳答案 您可以添加guard在函数的开头,如果参数无效则引发异常。例如:defmy_method(number)failArgumentError,"Inputshouldbegreaterthanorequalto2"ifnumbereputse.messageend#=>Inputshouldbegreaterthano
我没有找到太多关于如何执行此操作的信息,尽管有很多关于如何使用像这样的redirect_to将参数传递给重定向的建议:action=>'something',:controller=>'something'在我的应用程序中,我在路由文件中有以下内容match'profile'=>'User#show'我的表演Action是这样的defshow@user=User.find(params[:user])@title=@user.first_nameend重定向发生在同一个用户Controller中,就像这样defregister@title="Registration"@user=Use
对于作为String#tr参数的单引号字符串文字中反斜杠的转义状态,我觉得有些神秘。你能解释一下下面三个例子之间的对比吗?我特别不明白第二个。为了避免复杂化,我在这里使用了'd',在双引号中转义时不会改变含义("\d"="d")。'\\'.tr('\\','x')#=>"x"'\\'.tr('\\d','x')#=>"\\"'\\'.tr('\\\d','x')#=>"x" 最佳答案 在tr中转义tr的第一个参数非常类似于正则表达式中的括号字符分组。您可以在表达式的开头使用^来否定匹配(替换任何不匹配的内容)并使用例如a-f来匹配一
我正在使用RubyonRails3.0.9,我想生成一个传递一些自定义参数的link_toURL。也就是说,有一个articles_path(www.my_web_site_name.com/articles)我想生成如下内容:link_to'Samplelinktitle',...#HereIshouldimplementthecode#=>'http://www.my_web_site_name.com/articles?param1=value1¶m2=value2&...我如何编写link_to语句“alàRubyonRailsWay”以实现该目的?如果我想通过传递一些