在工作中发现,很多小伙伴使用线程池的时候,很容易犯的一个错误是:目的是每次请求过来,调用某个方法,在这个方法里面,调用线程池中的一个线程 比如:对某个方法的调用进行日志的调用,但是为了不影响主业务,所以就想开个线程来记录日志,又考虑其它方法也要记录日志,就想建一个线程池,重复利用记录日志的线程。 本来这个想法是没问题的,但是问题在于,在新建线程池的时候,没有考虑到创建单一的线程池的对象,而是每次调用记录日志的线程,都会创建一个线程池,这个就很致命,但是因为创建线程池的调用线程执行的方法被抽取成一个类,又不是很容易发现。
错误栗子如下
//线程池
package com.example.openfiegndemo.controller.threadpool;
import java.util.concurrent.*;
public class ThreaPool {
//创建一个线程池
public ThreadPoolExecutor getThreadPool() {
BlockingQueue blockingQueue = new LinkedBlockingQueue();
return new ThreadPoolExecutor(2, 10, 5, TimeUnit.SECONDS, blockingQueue);
}
//使用一个静态方法执行线程
public static void doSomething(){
new ThreaPool().getThreadPool().execute(new RunThread());
}
}
//线程对象
package com.example.openfiegndemo.controller.threadpool;
public class RunThread extends Thread{
@Override
public void run() {
System.out.println("这是我要执行的操作");
}
}
//测试类
package com.example.openfiegndemo.controller.threadpool;
public class TestThreadPool {
public static void main(String[] args) {
ThreaPool.doSomething();
}
}
上面这种使用线程池的方式,就是上面所说的情况,每次调用都去创建一个线程池,不仅浪费资源,而且浪费时间,并发高的情况下,还有可能出现OOM
解决问题的核心:每次使用的都是同一个线程池,保证线程池的单例 解决方式一: 把创建对象交给spring容器来管理,保证唯一
//线程池
package com.example.openfiegndemo.controller.threadpool;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@Configuration
public class ThreadPoolConfig {
@Bean("threadPoolExecutor")
public ThreadPoolExecutor getThreadPool() {
BlockingQueue blockingQueue = new LinkedBlockingQueue();
return new ThreadPoolExecutor(2, 10, 5, TimeUnit.SECONDS, blockingQueue);
}
}
//处理事务的线程类
package com.example.openfiegndemo.controller.threadpool;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.concurrent.ThreadPoolExecutor;
@Component
public class RunThreadBean{
@Resource(name="threadPoolExecutor")
private ThreadPoolExecutor threadPoolExecutor;
public void dosomething(){
threadPoolExecutor.execute(new Runnable() {
@Override
public void run() {
System.out.println("我做了一些事情");
}
});
}
}
package com.example.openfiegndemo.controller.threadpool;
//测试类
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
@SpringBootTest
@RunWith(SpringRunner.class)
public class TestThreadPool {
@Resource
private RunThreadBean runThreadBean;
public static void main(String[] args) {
ThreaPool.doSomething();
}
@Test
public void test(){
runThreadBean.dosomething();
}
}
还有一种方式就是使用单例模式创建一个线程池,单例模式有各种实现方式,这里就使用最简单一点的饿汉式来创建
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class SingleThreadPool {
private static final ThreadPoolExecutor threadPoolExecutor= new ThreadPoolExecutor(2, 10, 5, TimeUnit.SECONDS, new LinkedBlockingQueue());
public static void dosomething(){
threadPoolExecutor.execute(new Runnable() {
@Override
public void run() {
System.out.println("我做了一些事");
}
});
}
}