溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何通過(guò)實(shí)現(xiàn)ThreadFactory來(lái)對(duì)線程進(jìn)行命名

發(fā)布時(shí)間:2021-06-11 09:53:19 來(lái)源:億速云 閱讀:539 作者:小新 欄目:開(kāi)發(fā)技術(shù)

這篇文章主要介紹如何通過(guò)實(shí)現(xiàn)ThreadFactory來(lái)對(duì)線程進(jìn)行命名,文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!

本文記錄@Async的基本使用以及通過(guò)實(shí)現(xiàn)ThreadFactory來(lái)實(shí)現(xiàn)對(duì)線程的命名。

@Async的基本使用

近日有一個(gè)道友提出到一個(gè)問(wèn)題,大意如下:

業(yè)務(wù)場(chǎng)景需要進(jìn)行批量更新,已有數(shù)據(jù)id主鍵、更新的狀態(tài)。單條更新性能太慢,所以使用in進(jìn)行批量更新。但是會(huì)導(dǎo)致鎖表使得其他業(yè)務(wù)無(wú)法訪問(wèn)該表,in的量級(jí)太低又導(dǎo)致性能太慢。

龍道友提出了一個(gè)解決方案,把要處理的數(shù)據(jù)分成幾個(gè)list之后使用多線程進(jìn)行數(shù)據(jù)更新。提到多線程可直接使用@Async注解來(lái)進(jìn)行異步操作。

好的,接下來(lái)上面的問(wèn)題我們不予解答,來(lái)說(shuō)下@Async的簡(jiǎn)單使用

@Async在SpringBoot中的使用較為簡(jiǎn)單,所在位置如下:

一.啟動(dòng)類加入注解@EnableAsync

第一步就是在啟動(dòng)類中加入@EnableAsync注解   啟動(dòng)類代碼如下:

@SpringBootApplication
@EnableAsync
public class EurekaApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaApplication.class, args);
    }
}

二.編寫(xiě)MyAsyncConfigurer類

MyAsyncConfigurer類實(shí)現(xiàn)了AsyncConfigurer接口,重寫(xiě)AsyncConfigurer接口的兩個(gè)重要方法:

1.getAsyncExecutor:自定義線程池,若不重寫(xiě)會(huì)使用默認(rèn)的線程池。

2.getAsyncUncaughtExceptionHandler:捕捉IllegalArgumentException異常.

一方法很好理解。二方法中提到的IllegalArgumentException異常在之后會(huì)說(shuō)明。代碼如下:

/**
 * @author hsw
 * @Date 20:12 2018/8/23
 */
@Slf4j
@Component
public class MyAsyncConfigurer implements AsyncConfigurer {
 
    @Override
    public Executor getAsyncExecutor() {
        //定義一個(gè)最大為10個(gè)線程數(shù)量的線程池
        ExecutorService service = Executors.newFixedThreadPool(10);
        return service;
    }
 
    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new MyAsyncExceptionHandler();
    }
 
    class MyAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
 
        @Override
        public void handleUncaughtException(Throwable throwable, Method method, Object... objects) {
            log.info("Exception message - " + throwable.getMessage());
            log.info("Method name - " + method.getName());
            for (Object param : objects) {
                log.info("Parameter value - " + param);
            }
        }
    }
}</code>

三.三種測(cè)試方法

1.無(wú)參無(wú)返回值方法

2.有參無(wú)返回值方法

3.有參有返回值方法

具體代碼如下:

/**
 * @author hsw
 * @Date 20:07 2018/8/23
 */
@Slf4j
@Component
public class AsyncExceptionDemo {
 
    @Async
    public void simple() {
        log.info("this is a void method");
    }
 
    @Async
    public void inputDemo (String s) {
        log.info("this is a input method,{}",s);
        throw new IllegalArgumentException("inputError");
    }
 
    @Async
    public Future hardDemo (String s) {
        log.info("this is a hard method,{}",s);
        Future future;
        try {
            Thread.sleep(3000);
            throw new IllegalArgumentException();
        }catch (InterruptedException e){
            future = new AsyncResult("InterruptedException error");
        }catch (IllegalArgumentException e){
            future = new AsyncResult("i am throw IllegalArgumentException error");
        }
        return future;
    }
}

在第二種方法中,拋出了一種名為IllegalArgumentException的異常,在上述第二步中,我們已經(jīng)通過(guò)重寫(xiě)getAsyncUncaughtExceptionHandler方法,完成了對(duì)產(chǎn)生該異常時(shí)的處理。

在處理方法中我們簡(jiǎn)單列出了異常的各個(gè)信息。

ok,現(xiàn)在該做的準(zhǔn)備工作都已完成,加個(gè)測(cè)試方法看看結(jié)果如何

/**
 * @author hsw
 * @Date 20:16 2018/8/23
 */
@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class AsyncExceptionDemoTest {
 
    @Autowired
    private AsyncExceptionDemo asyncExceptionDemo;
 
    @Test
    public void simple() {
        for (int i=0;i<3;i++){
            try {
                asyncExceptionDemo.simple();
                asyncExceptionDemo.inputDemo("input");
                Future future = asyncExceptionDemo.hardDemo("hard");
                log.info(future.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
    }
}

測(cè)試結(jié)果如下:

2018-08-25 16:25:03.856  INFO 4396 --- [           main] c.hsw.test.async.AsyncExceptionDemoTest  : Started AsyncExceptionDemoTest in 3.315 seconds (JVM running for 4.184)
2018-08-25 16:25:03.947  INFO 4396 --- [pool-1-thread-1] com.hsw.test.async.AsyncExceptionDemo    : this is a void method
2018-08-25 16:25:03.947  INFO 4396 --- [pool-1-thread-3] com.hsw.test.async.AsyncExceptionDemo    : this is a hard method,hard
2018-08-25 16:25:03.947  INFO 4396 --- [pool-1-thread-2] com.hsw.test.async.AsyncExceptionDemo    : this is a input method,input
2018-08-25 16:25:03.947  INFO 4396 --- [pool-1-thread-2] com.hsw.test.async.MyAsyncConfigurer     : Exception message - inputError
2018-08-25 16:25:03.947  INFO 4396 --- [pool-1-thread-2] com.hsw.test.async.MyAsyncConfigurer     : Method name - inputDemo
2018-08-25 16:25:03.947  INFO 4396 --- [pool-1-thread-2] com.hsw.test.async.MyAsyncConfigurer     : Parameter value - input
2018-08-25 16:25:06.947  INFO 4396 --- [           main] c.hsw.test.async.AsyncExceptionDemoTest  : i am throw IllegalArgumentException error
2018-08-25 16:25:06.948  INFO 4396 --- [pool-1-thread-4] com.hsw.test.async.AsyncExceptionDemo    : this is a void method
2018-08-25 16:25:06.948  INFO 4396 --- [pool-1-thread-6] com.hsw.test.async.AsyncExceptionDemo    : this is a hard method,hard
2018-08-25 16:25:06.948  INFO 4396 --- [pool-1-thread-5] com.hsw.test.async.AsyncExceptionDemo    : this is a input method,input
2018-08-25 16:25:06.948  INFO 4396 --- [pool-1-thread-5] com.hsw.test.async.MyAsyncConfigurer     : Exception message - inputError
2018-08-25 16:25:06.948  INFO 4396 --- [pool-1-thread-5] com.hsw.test.async.MyAsyncConfigurer     : Method name - inputDemo
2018-08-25 16:25:06.948  INFO 4396 --- [pool-1-thread-5] com.hsw.test.async.MyAsyncConfigurer     : Parameter value - input
2018-08-25 16:25:09.949  INFO 4396 --- [           main] c.hsw.test.async.AsyncExceptionDemoTest  : i am throw IllegalArgumentException error
2018-08-25 16:25:09.950  INFO 4396 --- [pool-1-thread-7] com.hsw.test.async.AsyncExceptionDemo    : this is a void method
2018-08-25 16:25:09.950  INFO 4396 --- [pool-1-thread-8] com.hsw.test.async.AsyncExceptionDemo    : this is a input method,input
2018-08-25 16:25:09.950  INFO 4396 --- [pool-1-thread-8] com.hsw.test.async.MyAsyncConfigurer     : Exception message - inputError
2018-08-25 16:25:09.950  INFO 4396 --- [pool-1-thread-9] com.hsw.test.async.AsyncExceptionDemo    : this is a hard method,hard
2018-08-25 16:25:09.950  INFO 4396 --- [pool-1-thread-8] com.hsw.test.async.MyAsyncConfigurer     : Method name - inputDemo
2018-08-25 16:25:09.950  INFO 4396 --- [pool-1-thread-8] com.hsw.test.async.MyAsyncConfigurer     : Parameter value - input
2018-08-25 16:25:12.950  INFO 4396 --- [           main] c.hsw.test.async.AsyncExceptionDemoTest  : i am throw IllegalArgumentException error
2018-08-25 16:25:12.953  INFO 4396 --- [       Thread-2] o.s.w.c.s.GenericWebApplicationContext   : Closing org.springframework.web.context.support.GenericWebApplicationContext@71075444: startup date [Sat Aug 25 16:25:01 CST 2018]; root of context hierarchy

測(cè)試成功。

到此為止,關(guān)于@Async注解的基本使用內(nèi)容結(jié)束。但是在測(cè)試過(guò)程中,我注意到線程名的命名為默認(rèn)的pool-1-thread-X,雖然可以分辨出不同的線程在進(jìn)行作業(yè),但依然很不方便,為什么默認(rèn)會(huì)以這個(gè)命名方式進(jìn)行命名呢?

線程池的命名

點(diǎn)進(jìn)Executors.newFixedThreadPool,發(fā)現(xiàn)在初始化線程池的時(shí)候有另一種方法

public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory)

那么這個(gè)ThreadFactory為何方神圣?繼續(xù)ctrl往里點(diǎn),在類中發(fā)現(xiàn)這么一個(gè)實(shí)現(xiàn)類。

破案了破案了。原來(lái)是在這里對(duì)線程池進(jìn)行了命名。那我們只需自己實(shí)現(xiàn)ThreadFactory接口,對(duì)命名方法進(jìn)行重寫(xiě)即可,完成代碼如下:

static class MyNamedThreadFactory 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;
    MyNamedThreadFactory(String name) {
 
        SecurityManager s = System.getSecurityManager();
        group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
        if (null == name || name.isEmpty()) {
            name = "pool";
        }
 
        namePrefix = name + "-" + 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;
    }
}

然后在創(chuàng)建線程池的時(shí)候加上新寫(xiě)的類即可對(duì)線程名進(jìn)行自定義。

@Override
public Executor getAsyncExecutor() {
    ExecutorService service = Executors.newFixedThreadPool(10,new MyNamedThreadFactory("HSW"));
    return service;
}

看看重命名后的執(zhí)行結(jié)果。

2018-08-25 16:42:44.068  INFO 46028 --- [           main] c.hsw.test.async.AsyncExceptionDemoTest  : Started AsyncExceptionDemoTest in 3.038 seconds (JVM running for 3.83)
2018-08-25 16:42:44.155  INFO 46028 --- [ HSW-1-thread-3] com.hsw.test.async.AsyncExceptionDemo    : this is a hard method,hard
2018-08-25 16:42:44.155  INFO 46028 --- [ HSW-1-thread-2] com.hsw.test.async.AsyncExceptionDemo    : this is a input method,input
2018-08-25 16:42:44.155  INFO 46028 --- [ HSW-1-thread-1] com.hsw.test.async.AsyncExceptionDemo    : this is a void method
2018-08-25 16:42:44.155  INFO 46028 --- [ HSW-1-thread-2] com.hsw.test.async.MyAsyncConfigurer     : Exception message - inputError
2018-08-25 16:42:44.155  INFO 46028 --- [ HSW-1-thread-2] com.hsw.test.async.MyAsyncConfigurer     : Method name - inputDemo
2018-08-25 16:42:44.155  INFO 46028 --- [ HSW-1-thread-2] com.hsw.test.async.MyAsyncConfigurer     : Parameter value - input
2018-08-25 16:42:47.155  INFO 46028 --- [           main] c.hsw.test.async.AsyncExceptionDemoTest  : i am throw IllegalArgumentException error
2018-08-25 16:42:47.156  INFO 46028 --- [ HSW-1-thread-4] com.hsw.test.async.AsyncExceptionDemo    : this is a void method
2018-08-25 16:42:47.156  INFO 46028 --- [ HSW-1-thread-5] com.hsw.test.async.AsyncExceptionDemo    : this is a input method,input
2018-08-25 16:42:47.156  INFO 46028 --- [ HSW-1-thread-6] com.hsw.test.async.AsyncExceptionDemo    : this is a hard method,hard
2018-08-25 16:42:47.156  INFO 46028 --- [ HSW-1-thread-5] com.hsw.test.async.MyAsyncConfigurer     : Exception message - inputError
2018-08-25 16:42:47.156  INFO 46028 --- [ HSW-1-thread-5] com.hsw.test.async.MyAsyncConfigurer     : Method name - inputDemo
2018-08-25 16:42:47.156  INFO 46028 --- [ HSW-1-thread-5] com.hsw.test.async.MyAsyncConfigurer     : Parameter value - input
2018-08-25 16:42:50.156  INFO 46028 --- [           main] c.hsw.test.async.AsyncExceptionDemoTest  : i am throw IllegalArgumentException error
2018-08-25 16:42:50.157  INFO 46028 --- [ HSW-1-thread-7] com.hsw.test.async.AsyncExceptionDemo    : this is a void method
2018-08-25 16:42:50.157  INFO 46028 --- [ HSW-1-thread-8] com.hsw.test.async.AsyncExceptionDemo    : this is a input method,input
2018-08-25 16:42:50.157  INFO 46028 --- [ HSW-1-thread-9] com.hsw.test.async.AsyncExceptionDemo    : this is a hard method,hard
2018-08-25 16:42:50.157  INFO 46028 --- [ HSW-1-thread-8] com.hsw.test.async.MyAsyncConfigurer     : Exception message - inputError
2018-08-25 16:42:50.157  INFO 46028 --- [ HSW-1-thread-8] com.hsw.test.async.MyAsyncConfigurer     : Method name - inputDemo
2018-08-25 16:42:50.157  INFO 46028 --- [ HSW-1-thread-8] com.hsw.test.async.MyAsyncConfigurer     : Parameter value - input
2018-08-25 16:42:53.157  INFO 46028 --- [           main] c.hsw.test.async.AsyncExceptionDemoTest  : i am throw IllegalArgumentException error
2018-08-25 16:42:53.161  INFO 46028 --- [       Thread-2] o.s.w.c.s.GenericWebApplicationContext   : Closing org.springframework.web.context.support.GenericWebApplicationContext@71075444: startup date [Sat Aug 25 16:42:41 CST 2018]; root of context hierarchy

Spring Boot使用@Async實(shí)現(xiàn)異步調(diào)用:自定義線程池

定義線程池

第一步,先在Spring Boot主類中定義一個(gè)線程池,比如:

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
    @EnableAsync
    @Configuration
    class TaskPoolConfig {
        @Bean("taskExecutor")
        public Executor taskExecutor() {
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            executor.setCorePoolSize(10);
            executor.setMaxPoolSize(20);
            executor.setQueueCapacity(200);
            executor.setKeepAliveSeconds(60);
            executor.setThreadNamePrefix("taskExecutor-");
            executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
            return executor;
        }
    }
}

上面我們通過(guò)使用ThreadPoolTaskExecutor創(chuàng)建了一個(gè)線程池,同時(shí)設(shè)置了以下這些參數(shù):

  • 核心線程數(shù)10:線程池創(chuàng)建時(shí)候初始化的線程數(shù)

  • 最大線程數(shù)20:線程池最大的線程數(shù),只有在緩沖隊(duì)列滿了之后才會(huì)申請(qǐng)超過(guò)核心線程數(shù)的線程

  • 緩沖隊(duì)列200:用來(lái)緩沖執(zhí)行任務(wù)的隊(duì)列

  • 允許線程的空閑時(shí)間60秒:當(dāng)超過(guò)了核心線程出之外的線程在空閑時(shí)間到達(dá)之后會(huì)被銷毀

  • 線程池名的前綴:設(shè)置好了之后可以方便我們定位處理任務(wù)所在的線程池

  • 線程池對(duì)拒絕任務(wù)的處理策略:這里采用了CallerRunsPolicy策略,當(dāng)線程池沒(méi)有處理能力的時(shí)候,該策略會(huì)直接在 execute 方法的調(diào)用線程中運(yùn)行被拒絕的任務(wù);如果執(zhí)行程序已關(guān)閉,則會(huì)丟棄該任務(wù)

使用線程池

在定義了線程池之后,我們?nèi)绾巫尞惒秸{(diào)用的執(zhí)行任務(wù)使用這個(gè)線程池中的資源來(lái)運(yùn)行呢?方法非常簡(jiǎn)單,我們只需要在@Async注解中指定線程池名即可,比如:

@Slf4j
@Component
public class Task {
    public static Random random = new Random();
    @Async("taskExecutor")
    public void doTaskOne() throws Exception {
        log.info("開(kāi)始做任務(wù)一");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("完成任務(wù)一,耗時(shí):" + (end - start) + "毫秒");
    }
    @Async("taskExecutor")
    public void doTaskTwo() throws Exception {
        log.info("開(kāi)始做任務(wù)二");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("完成任務(wù)二,耗時(shí):" + (end - start) + "毫秒");
    }
    @Async("taskExecutor")
    public void doTaskThree() throws Exception {
        log.info("開(kāi)始做任務(wù)三");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("完成任務(wù)三,耗時(shí):" + (end - start) + "毫秒");
    }
}

單元測(cè)試

最后,我們來(lái)寫(xiě)個(gè)單元測(cè)試來(lái)驗(yàn)證一下

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class ApplicationTests {
    @Autowired
    private Task task;
    @Test
    public void test() throws Exception {
        task.doTaskOne();
        task.doTaskTwo();
        task.doTaskThree();
        Thread.currentThread().join();
    }
}

執(zhí)行上面的單元測(cè)試,我們可以在控制臺(tái)中看到所有輸出的線程名前都是之前我們定義的線程池前綴名開(kāi)始的,說(shuō)明我們使用線程池來(lái)執(zhí)行異步任務(wù)的試驗(yàn)成功了!

2018-03-27 22:01:15.620  INFO 73703 --- [ taskExecutor-1] com.didispace.async.Task                 : 開(kāi)始做任務(wù)一
2018-03-27 22:01:15.620  INFO 73703 --- [ taskExecutor-2] com.didispace.async.Task                 : 開(kāi)始做任務(wù)二
2018-03-27 22:01:15.620  INFO 73703 --- [ taskExecutor-3] com.didispace.async.Task                 : 開(kāi)始做任務(wù)三
2018-03-27 22:01:18.165  INFO 73703 --- [ taskExecutor-2] com.didispace.async.Task                 : 完成任務(wù)二,耗時(shí):2545毫秒
2018-03-27 22:01:22.149  INFO 73703 --- [ taskExecutor-3] com.didispace.async.Task                 : 完成任務(wù)三,耗時(shí):6529毫秒
2018-03-27 22:01:23.912  INFO 73703 --- [ taskExecutor-1] com.didispace.async.Task                 : 完成任務(wù)一,耗時(shí):8292毫秒

以上是“如何通過(guò)實(shí)現(xiàn)ThreadFactory來(lái)對(duì)線程進(jìn)行命名”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對(duì)大家有幫助,更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI