构建函数的参数说明
JAVA 创建线程池七个参数作用及学习总结
手动创建示例
依旧是在 main 方法中做示例
1.全参构造
定义如下:
注意这个是在方法外定义 为了多个方法使用同一个线程池以免浪费,由于我是在 main中做测试故需要定义为 static,一般使用时如果放在 spring InitializingBean 或其他初始话动作中的话 不必添加 static,如果在静态代码块中初始化则需要 static
private static ThreadPoolExecutor threadPoolExecutor = null;
main 中做初始化以及使用
threadPoolExecutor = new ThreadPoolExecutor(10, 20, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(500), Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());
threadPoolExecutor.submit(new BusinessThread(5));
threadPoolExecutor.shutdown();
简单解读:
这里 第一行的第一个构建参数即为核心线程数(这里我设置的10),即使空闲不在使用中也不会销毁,除非设置了核心线程超时时间的参数 即allowCoreThreadTimeOut 核心线程数即这个线程池的最小线程数。
第一行第二个构建参数,即最大线程数 当核心线程数中的所有线程没有空闲时,再提交任务时会进入工作队列等待,当工作队列也满了时,会再次创建一个新的线程,并从工作队列的首部取出一个任务交给新创建的线程去执行,新来的任务则进入工作队列,插入队尾(相当于排队等待),但通过这样的方式创建的线程是存在上限的,最大线程数就是这个上限。当最大线程数也上限了怎么办?继续往下看,JDK 也给了相应的几种解决策略供使用。
第一行第三个构建参数,第四个构建参数,即空闲线程等待时间,等待时间的单位,这里空闲线程指的并不是核心线程,而是上面通过最大线程数的方式,由于工作队列以及核心线程数均满时额外创建的线程,当这些线程空闲下来时,并等待到了指定的时间时依旧没有被使用,那么这些线程会被销毁。
第一行第五个构建参数,即 工作队列的类型以及队列的大小,目前提供四种工作队列,这里我简单说,详细见最上方参数链接
ArrayBlockingQueue
有界阻塞队列,先进先出,新任务会放到队尾就是最后。核心线程没有空闲时,提交新任务则会放入队列的队尾,等待。
如果队列已满,则创建一个新线程,如果线程数量已经达到maxPoolSize,则会执行拒绝策略。
LinkedBlockingQuene
无界阻塞队列(其实最大容量为Interger.MAX),先进先出。由于该队列的近似无界性(即没有上限),核心线程没有空闲时提交新任务会一直存入该队列,而不去创建新线程直到达到最大线程数,因此使用该工作队列时,参数 maxPoolSize 其实无用。
SynchronousQuene
不缓存任务的阻塞队列,生产者放入一个任务必须等到消费者取出这个任务。
也就是说新任务进来时不会缓存,直接执行该任务,如果没有可用线程则创建新线程,如果线程数量达到 maxPoolSize,则执行拒绝策略。
PriorityBlockingQueue
具有优先级的无界阻塞队列,优先级通过参数Comparator实现。
第一行第六个构建参数,即构建新线程的方式,一般使用默认的 factory(工厂)制造线程即可,一般是对当前线程做一些命名以及是否守护线程的属性赋值。如果有自己的需求可以参考源码的方式创建一个简单的 ThreadFactory ,下面一会儿会有一些例子提供参考。这里我使用的JDK 里面提供的默认创建方式,一般也够用了。
第一行第七个构建参数,即拒绝策略,上面队列中一再说明了,当核心已满,队列已满,最大线程数也满的情况下会执行的拒绝策略,简单来说就是当定义的线程池已经没有能力处理新的任务时,再有新的任务提交时该怎么去处理,JDK 同样提供了四种拒绝策略:
CallerRunsPolicy
在调用者线程中直接执行被拒绝任务的run方法,除非线程池已经shutdown,则直接抛弃任务
这里调用者指的是提交任务的线程,即执行提交任务代码当时所在的线程。
AbortPolicy
丢弃,并抛出RejectedExecutionException异常,一般提供的已有线程池大多都会选择这一种
DiscardPolicy
直接丢弃,什么都不做
DiscardOldestPolicy
抛弃进入队列最早的任务,然后尝试把这次拒绝的任务加入队列
第二行:
提交一个线程任务,这里 new 的类为自定义的类,并使用了改类的某一个构造函数,该类继承 thread,或者 实现 runnable ,提交到线程池种会执行 重写的 run 方法的内容。
第三行:
关闭当前线程池,会等待已有任务的执行完成,但是新的任务无法再提交。
其它构造方法
这里JDK 是 1.8版本;
可见JDK 提供的构造函数包括最后一个全参构造一共有四种
第一种舍弃了线程构建以及拒绝策略,
第二种舍弃了拒绝策略,
第三种线程构造,
第一种:
可见 舍弃的线程构造以及拒绝策略均采用了默认的方式
而拒绝策略默认为
abort 直接丢弃并抛异常
第二种
类似的使用了默认的拒绝策略
第三种
类似的使用了默认的线程构造
定义线程构建方式(名称或否守护线程)ThreadFactory
第一种方式:
lambda
threadPoolExecutor = new ThreadPoolExecutor(10, 20, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(500),
r->new Thread(r, "my-taskPoolThreadA-"+r.hashCode()) , new ThreadPoolExecutor.CallerRunsPolicy());
构建方式为:
r->new Thread(r, “my-taskPoolThreadA-”+r.hashCode())
运行后
第二种,仿照源码的方式创建:
可以参考源码中默认工厂的创建方式,查看路径如下:
进入
再进入 new 的类
可见自己创建一个工厂时需要实现 ThreadFactory 的接口
做部分解读如下:
实际自己构造时完全可以先照搬下来只改改命名方式:
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
public class MyThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
MyThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "my-pool-" +
poolNumber.getAndIncrement() +
"-thread-";
}
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
实际上我们完全可以根据自己的需要决定是否设置保护线程,即 重写的方法中 newThread(Runnable r) 中 内部属性我们可以通过我们设定的其他值来控制,可以整改添加一些构造函数,将一些我们想要的控制项通过构造方法或者其他方法的方式传入Factory 并在重写的方法内生效,使之得到应用。
例如可以如下修改:
为了外部调用构造函数定义为 public
public class MyThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
private boolean nDaemon;
public MyThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "my-pool-" +
poolNumber.getAndIncrement() +
"-thread-";
}
public MyThreadFactory(String prefix, boolean needDaemon){
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = prefix + "-mythread-" + poolNumber.getAndIncrement();
nDaemon = needDaemon;
}
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (nDaemon)
t.setDaemon(nDaemon);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
如此一来可以在外部如下书写:
threadPoolExecutor = new ThreadPoolExecutor(10, 20, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(500),
new MyThreadFactory("ACCName", false), new ThreadPoolExecutor.CallerRunsPolicy());
今天的文章java使用线程池创建线程_线程池创建后怎么调用分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/86687.html