java高并发之多线程踩坑
我们都知道在java语言中,开启线程是继承Thead或者实现Runnable接口,在高级一点我们使用Thread线程池。接下来,我要说的就是Thread线程池踩坑。
首先,需要注意的是什么场景需要使用线程池,使用哪个线程池,使用线程池需要注意哪些地方。
我们使用多线程是为了让多核cpu处理器更好的发光发热。如果是单核cpu开多了线程也不会有实际效果。所以我们在使用多线程要考虑机器的配置。一般情况,我们开cpu的2到3倍线程,开多了会增加系统调度开销处理不过来,开少了不能充分利用cpu。实际上,如果是耗时高的任务,可以这样处理。耗时短的比如过io操作,我们可以多开一些线程的。 还有,我们在服务中可以自定义一个全局线程池,这样子,可以更加方便的管理,更好的控制服务线程的数量。但是,如果有不同场景,比如耗时长的和耗时短的还是应该分成两个线程池单独管理比较好。下面我们看一下线程池使用方式。
线程池使用方式:
线程池需要实现Executor接口,划重点(线程池大小,队列长度和拒绝策略)
### 1、使用excutors类定义(不够灵活)
Executors.defaultThreadFactory(); //默认工厂
Executors.newCachedThreadPool(); //根据需要无限阔成
Executors.newFixedThreadPool(); //创建固定数量线程池
Executors.newScheduledThreadPool() //创建定时器线程池
2、使用ThreadPoolExecutor或者AsyncTaskExecutor(spring)自定义线程池
(1)ThreadPoolExecutor
private static final ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 5, 60L, TimeUnit.MINUTES,
new LinkedBlockingQueue<>(1000), new ThreadPoolExecutor.CallerRunsPolicy());
还有其他构造方式就不一一介绍了
(2)使用Spring线程池AsyncTaskExecutor
@Configuration
@EnableAsync
public class ThreadPoolConfiguration {
@Bean
@Primary
public AsyncTaskExecutor asyncServiceExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//核心线程数量:低于此值会重新创建
executor.setCorePoolSize(5);
//任务队列:核心线程处于工作中,会先放到队列中缓存
executor.setQueueCapacity(1000);
//线程池最大数量:任务队列已满时,最多创建的线程数量
executor.setMaxPoolSize(100);
//线程超时时间:超出核心线程数量的线程,在空闲一定的时间后退出
executor.setKeepAliveSeconds(30);
//线程池已满,队列已满时。新的任务处理策略:丢弃、执行、忽略、
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
return executor;
}
}
3、我们自定义线程池工厂类
/**
* The default thread factory
*/
static class DefaultThreadFactory 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;
DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "pool-" +
poolNumber.getAndIncrement() +
"-thread-";
}
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;
}
}
这是线程池默认工厂类,我们可以把它重写自定义,一般就是修改一下名字,日志啥的。
4、线程池拒绝策略:
我们在使用的时候,一般只注意线程的数量,最大线程数量,线程存活时间参数。 忘掉最重要两个参数,
阻塞队列和线程池拒绝策略。ThreadPoolExecutor线程池队列默认是无界队列如果我们不设置长度,
极端情况会把我们系统给撑死。ok,为了不把服务累死,我们把队列长度设置了,
此时,拒绝策略开始发挥作用了。默认拒绝策略是 AbortPolicy:这样当线程池满了,队列满了,
会把任务丢弃适合允许丢失追求性能的场景。
//线程池已满,队列已满时。新的任务处理策略:丢弃、执行、忽略
//AbortPolicy:丢弃
//DiscardPolicy:忽略
//CallerRunsPolicy:立即运行
//DiscardOldestPolicy:压进队列最后一位(踢出第一位)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。