The harm of improper use of thread pool (1): How to use local variable thread pool and class variable thread pool

Foreword:

  I don’t know if you have the following doubts when using the thread pool. This is the code I saw from some colleagues. They use the thread pool like this:

1. Thread pool local variables, new is used, and there is no manual shutdown.
2. Thread pool local variables, new is used, and finally manual shutdown is performed.
3. The thread pool is defined as a static type for class reuse.

Let's first think about which method is correct and what problems the wrong method may cause. Let's read this article with questions.

  This article explores the pitfalls in the use of local variable thread pools and class variable thread pools , and the problems that may result. Finally, based on experience and actual online projects, I give my current experience in using thread pools, as well as some changes in creating thread pools, for everyone to learn from.

Method 1: Use the thread pool locally, without shutdown

  First of all, we make it clear: from the thread pool of the local variable new, each thread of the program that executes this code will create a local thread pool. Not to mention the magical purpose for each thread to create a thread pool, the reusable nature of the thread pool is broken first. The created thread pool cannot be reused, so why bother to spend a lot of effort to create a thread pool?

Therefore, the local use of the thread pool itself is not recommended!

  Secondly, let's think about it again. What problems will occur if we use the thread pool locally, set the core thread to not be 0, and set allowCoreThreadTimeOut=false (the core thread pool will not be recycled after idle)? (Don’t even think about it, the core thread pool cannot be recycled, which will naturally lead to OOM)

Here is the problem code:

public static void main(String[] args) {
    
    
        while (true) {
    
    
            try {
    
    

             //newFixedThreadPool不会回收核心线程 可能导致OOM
                ExecutorService service = Executors.newFixedThreadPool(1);
                service.submit(new Runnable() {
    
    
                    @Override
                    public void run() {
    
    
                        try {
    
    
                            Thread.sleep(2000); 模拟处理业务
                        } catch (InterruptedException e) {
    
    
                        }
                    }
                });
                service = null;
            } catch (Exception e) {
    
    
            }
            try {
    
    
                Thread.sleep(2000);
                System.gc();
            } catch (InterruptedException e) {
    
    
            }
        }
    }

  So, wouldn’t it be good if we set the core thread to be recyclable? Problems can arise as well. The system may show a regular trend of memory usage rising, then falling, then rising again, and then falling again based on the thread expiration time you set. Can you tell me this is a good memory operation?

Method 2: Use the thread pool locally and manually shut down the thread pool after use.

  Okok, this method reduces the risk of OOM, but it is used locally. Why do you use the thread pool locally? Doesn't this cause each thread to create a new thread pool, causing the thread pool to not be reused? What is the difference between this and you not using a thread pool? The system also wastes resources to create a thread pool.

Method 3: Define the thread pool as a static type for class reuse

  Obviously, this is the correct way to use the thread pool. The statically modified class variable will only be loaded once, and all threads share this thread pool. Here is the correct usage code:

/**
 * @author: 代码丰
 * @Description:
 */
public class staticTestExample {
    
    
    //static 定义线程池
    private static ThreadPoolExecutor pool = new ThreadPoolExecutor(
            corePoolSize,
            maximumPoolSize,
            timeout,
            TimeUnit.SECONDS, 
            new LinkedBlockingDeque<>(), 
            new ThreadPoolExecutor.AbortPolicy());
   
    public static void main(String[] args) {
    
    
        //使用线程池
        Future<Boolean> submit = pool.submit(() -> true);
    }
}

Reuse of thread pools in business

  Here are the following two ideas

The first one: Create a thread pool based on the business execution type (the same type of business reuses a thread pool).
  Simply put, where the business scenarios are the same and a thread pool is needed, a thread pool is reused. For example, if you split a task scenario and need to split 100 tasks for execution at one time, you can hand over these 100 tasks of the same business scenario to a specifically named thread pool for processing. This thread pool is specifically designed to handle task splitting.

code show as below:

/**
 * @author 代码丰
 * 创建线程池工具类
 */
public class ThreadPoolUtil {
    
    
    private static final Map<String, ExecutorService> POOL_CATCH = Maps.newConcurrentMap();

    /**
     * 根据特定名称去缓存线程池
     * @param poolName
     * @return
     */
    public static ExecutorService create(String poolName) {
    
    
        //map有缓存直接复用缓存中存在的线程池
        if (POOL_CATCH.containsKey(poolName)) {
    
    
            return POOL_CATCH.get(poolName);
        }
        // synchronized锁字符串常量池字段 防止并发,使得map中缓存的线程池,只会创建一次
        synchronized (poolName.intern()) {
    
    
            int poolSize = Runtime.getRuntime().availableProcessors() * 2;
            ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(poolSize, poolSize,
                    30L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(),new ThreadPoolExecutor.AbortPolicy());
            POOL_CATCH.put(poolName, threadPoolExecutor);
            return threadPoolExecutor;
        }
    }
}

	/**
 * @author: 代码丰
 */
public class CorrectTest {
    
    
    public static void main(String[] args) {
    
    
        ExecutorService pool = ThreadPoolUtil.create("拆分任务线程池");
        //线程池执行任务
        Future<Boolean> submit = pool.submit(() -> true);
    }
}

The second type: Create a thread pool based on the nature of user login (reuse a thread pool for one type of user).
  Simply put, if the user type and business scenario are the same, and a thread pool is needed, a thread pool is reused.

/**
 * @author: 代码丰
 * @Description:
 */
public class CorrectTest {
    
    
    public static void main(String[] args) {
    
    
        //模拟得到用户信息
        Userinfo  = getUserinfo();
        //模拟用相同的用户类型(type)去创建线程池
        ExecutorService pool = ThreadPoolUtil.create(Userinfo.getType);
        //线程池执行任务
        Future<Boolean> submit = pool.submit(() -> true);
    }
}

Summarize

  1. Use a global thread pool instead of a local thread pool, otherwise there may be an OOM risk of continuously creating local thread pools
  2. Even if you use a local thread pool, you must shut down at the end, otherwise it may cause memory leaks that do not recycle core threads.
  3. Understand that thread pools are for reuse, do not create a local thread pool randomly in the code

Guess you like

Origin blog.csdn.net/qq_44716086/article/details/128922458