关于线程池的使用极容易犯的错误

527 阅读2分钟

在工作中发现,很多小伙伴使用线程池的时候,很容易犯的一个错误是:目的是每次请求过来,调用某个方法,在这个方法里面,调用线程池中的一个线程 比如:对某个方法的调用进行日志的调用,但是为了不影响主业务,所以就想开个线程来记录日志,又考虑其它方法也要记录日志,就想建一个线程池,重复利用记录日志的线程。 本来这个想法是没问题的,但是问题在于,在新建线程池的时候,没有考虑到创建单一的线程池的对象,而是每次调用记录日志的线程,都会创建一个线程池,这个就很致命,但是因为创建线程池的调用线程执行的方法被抽取成一个类,又不是很容易发现。

错误栗子如下

//线程池
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("我做了一些事");
            }
        });
    }
}