爲什麼不推薦使用Executors
底層確實是通過LinkedBlockingQueue實現的,默認不設置隊列大小的話,將是一個無邊界的阻塞隊列,最大長度爲Integer.MAX_VALUE,而創建這麼多線程,必然就有可能導致OOM,報錯如下:
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.util.concurrent.LinkedBlockingQueue.offer(LinkedBlockingQueue.java:416)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1371)
at com.hollis.ExecutorsDemo.main(ExecutorsDemo.java:16)
創建線程池的正確姿勢
public class ExecutorsDemo {
private static ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
.setNameFormat("demo-pool-%d").build();
private static ExecutorService pool = new ThreadPoolExecutor(5, 200,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());
public static void main(String[] args) {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
pool.execute(new SubThread());
}
}
}
關閉線程池
long start = System.currentTimeMillis();
for (int i = 0; i <= 5; i++) {
pool.execute(new Job());
}
pool.shutdown();
while (!pool.awaitTermination(1, TimeUnit.SECONDS)) {
LOGGER.info("線程還在執行。。。");
}
long end = System.currentTimeMillis();
LOGGER.info("一共處理了【{}】", (end - start));
SpringBoot 使用線程池
//線程池配置
@Configuration
public class TreadPoolConfig {
/**
* 消費隊列線程
* @return
*/
@Bean(value = "consumerQueueThreadPool")
public ExecutorService buildConsumerQueueThreadPool(){
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
.setNameFormat("consumer-queue-thread-%d").build();
ExecutorService pool = new ThreadPoolExecutor(5, 5, 0L, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<Runnable>(5),namedThreadFactory,new ThreadPoolExecutor.AbortPolicy());
return pool ;
}
}
//線程池使用
@Resource(name = "consumerQueueThreadPool")
private ExecutorService consumerQueueThreadPool;
@Override
public void execute() {
//消費隊列
for (int i = 0; i < 5; i++) {
consumerQueueThreadPool.execute(new ConsumerQueueThread());
}
}
線程池隔離
hystrix 隔離
首先需要定義兩個線程池,分別用於執行訂單、處理用戶。
/**
* Function:訂單服務
*
* @author crossoverJie
* Date: 2018/7/28 16:43
* @since JDK 1.8
*/
public class CommandOrder extends HystrixCommand<String> {
private final static Logger LOGGER = LoggerFactory.getLogger(CommandOrder.class);
private String orderName;
public CommandOrder(String orderName) {
super(Setter.withGroupKey(
//服務分組
HystrixCommandGroupKey.Factory.asKey("OrderGroup"))
//線程分組
.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("OrderPool"))
//線程池配置
.andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter()
.withCoreSize(10)
.withKeepAliveTimeMinutes(5)
.withMaxQueueSize(10)
.withQueueSizeRejectionThreshold(10000))
.andCommandPropertiesDefaults(
HystrixCommandProperties.Setter()
.withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD))
)
;
this.orderName = orderName;
}
@Override
public String run() throws Exception {
LOGGER.info("orderName=[{}]", orderName);
TimeUnit.MILLISECONDS.sleep(100);
return "OrderName=" + orderName;
}
}
/**
* Function:用戶服務
*
* @author crossoverJie
* Date: 2018/7/28 16:43
* @since JDK 1.8
*/
public class CommandUser extends HystrixCommand<String> {
private final static Logger LOGGER = LoggerFactory.getLogger(CommandUser.class);
private String userName;
public CommandUser(String userName) {
super(Setter.withGroupKey(
//服務分組
HystrixCommandGroupKey.Factory.asKey("UserGroup"))
//線程分組
.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("UserPool"))
//線程池配置
.andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter()
.withCoreSize(10)
.withKeepAliveTimeMinutes(5)
.withMaxQueueSize(10)
.withQueueSizeRejectionThreshold(10000))
//線程池隔離
.andCommandPropertiesDefaults(
HystrixCommandProperties.Setter()
.withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD))
)
;
this.userName = userName;
}
@Override
public String run() throws Exception {
LOGGER.info("userName=[{}]", userName);
TimeUnit.MILLISECONDS.sleep(100);
return "userName=" + userName;
}
}
模擬運行:
public static void main(String[] args) throws Exception {
CommandOrder commandPhone = new CommandOrder("手機");
CommandOrder command = new CommandOrder("電視");
//阻塞方式執行
String execute = commandPhone.execute();
LOGGER.info("execute=[{}]", execute);
//異步非阻塞方式
Future<String> queue = command.queue();
String value = queue.get(200, TimeUnit.MILLISECONDS);
LOGGER.info("value=[{}]", value);
CommandUser commandUser = new CommandUser("張三");
String name = commandUser.execute();
LOGGER.info("name=[{}]", name);
}
原理:利用一個 Map 來存放不同業務對應的線程池。
注意:自定義的 Command 並不是一個單例,每次執行需要 new 一個實例,不然會報 This instance can only be executed once. Please instantiate a new instance.
參考:如何優雅的使用和理解線程池
技術討論 & 疑問建議 & 個人博客
版權聲明: 本博客所有文章除特別聲明外,均採用 CC BY-NC-SA 3.0 許可協議,轉載請註明出處!