cosycode类分享:可控制的单循环线程 com.github.cosycode.common.thread.CtrlLoopThreadComp

本文主要是介绍cosycode类分享:可控制的单循环线程 com.github.cosycode.common.thread.CtrlLoopThreadComp,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

cosycode类分享:可控制的单循环线程

CtrlLoopThreadComp 是我在处理异步信息的时候封装的一个线程类, 简单来说就是对一个方法进行循环调用.

普通线程里面加 while 循环

我在开发的时候遇到一些在线程里面加while循环的例子, 如下

@Test
public void baseWhileTest() {Runnable runnable =  () -> {while (!Thread.currentThread().isInterrupted()) {// 执行相关逻辑log.info("执行相关逻辑");try {// 防止 CPU 被打满Thread.sleep(500);} catch (InterruptedException e) {Thread.currentThread().interrupt();log.info("线程从睡眠中唤醒");}}log.info("线程结束");};// 启动线程final Thread thread = new Thread(runnable, "while 循环线程");thread.start();// 一段时间后(5000毫秒后)暂停Throws.con(5000, Thread::sleep).logThrowable();thread.interrupt();
}

运行结果

[INFO] 2022-02-20 22:02:20.260 [while 循环线程] (CtrlLoopThreadCompTest.java:168) 执行相关逻辑
[INFO] 2022-02-20 22:02:20.769 [while 循环线程] (CtrlLoopThreadCompTest.java:168) 执行相关逻辑
[INFO] 2022-02-20 22:02:21.284 [while 循环线程] (CtrlLoopThreadCompTest.java:168) 执行相关逻辑
[INFO] 2022-02-20 22:02:21.803 [while 循环线程] (CtrlLoopThreadCompTest.java:168) 执行相关逻辑
[INFO] 2022-02-20 22:02:22.314 [while 循环线程] (CtrlLoopThreadCompTest.java:168) 执行相关逻辑
[INFO] 2022-02-20 22:02:22.828 [while 循环线程] (CtrlLoopThreadCompTest.java:168) 执行相关逻辑
[INFO] 2022-02-20 22:02:23.330 [while 循环线程] (CtrlLoopThreadCompTest.java:168) 执行相关逻辑
[INFO] 2022-02-20 22:02:23.837 [while 循环线程] (CtrlLoopThreadCompTest.java:168) 执行相关逻辑
[INFO] 2022-02-20 22:02:24.338 [while 循环线程] (CtrlLoopThreadCompTest.java:168) 执行相关逻辑
[INFO] 2022-02-20 22:02:24.839 [while 循环线程] (CtrlLoopThreadCompTest.java:168) 执行相关逻辑
[INFO] 2022-02-20 22:02:25.267 [while 循环线程] (CtrlLoopThreadCompTest.java:174) 线程从睡眠中唤醒
[INFO] 2022-02-20 22:02:25.267 [while 循环线程] (CtrlLoopThreadCompTest.java:177) 线程结束

上面是一个相当简单的例子,但事实上遇到的并没有这么简单,尤其是业余进行swing开发的时候,循环对消息事件进行处理的时候,就需要这个线程一会正常循环运行,一会暂停处理相关消息。还有的需要执行几次循环后暂停等等各种操作。

于是便封装了一个 CtrlLoopThreadComp 类,专门处理这种使用一个异步线程,额外去循环调用方法的操作。

CtrlLoopThreadComp 的循环调用代码

如下,就是使用 CtrlLoopThreadComp 类对上面的线程里面加while循环的代码的转译.

@Test
public void CoBaseTest1() {final CtrlLoopThreadComp threadComp = CtrlLoopThreadComp.ofRunnable(() -> {log.info("执行相关逻辑");})// 执行出现异常则继续下一次执行.catchFun(CtrlLoopThreadComp.CATCH_FUNCTION_CONTINUE)// 每 500 毫秒执行一次.setMillisecond(500)// 线程名称名为 COM-RUN.setName("可循环控制线程");// 启动线程log.info(" ======> 启动线程");threadComp.start();// 一段时间后(5000毫秒后)暂停Throws.con(5000, Thread::sleep).logThrowable();thread.interrupt();
}

运行结果

[INFO] 2022-02-20 21:59:56.414 [main] (CtrlLoopThreadCompTest.java:204)  ======> 启动线程
[DEBUG] 2022-02-20 21:59:56.414 [可循环控制线程] (CtrlLoopThreadComp.java:425) CtrlLoopThread [可循环控制线程] start!!!
[INFO] 2022-02-20 21:59:56.414 [可循环控制线程] (CtrlLoopThreadCompTest.java:194) 执行相关逻辑
[INFO] 2022-02-20 21:59:56.928 [可循环控制线程] (CtrlLoopThreadCompTest.java:194) 执行相关逻辑
[INFO] 2022-02-20 21:59:57.443 [可循环控制线程] (CtrlLoopThreadCompTest.java:194) 执行相关逻辑
[INFO] 2022-02-20 21:59:57.944 [可循环控制线程] (CtrlLoopThreadCompTest.java:194) 执行相关逻辑
[INFO] 2022-02-20 21:59:58.450 [可循环控制线程] (CtrlLoopThreadCompTest.java:194) 执行相关逻辑
[INFO] 2022-02-20 21:59:58.960 [可循环控制线程] (CtrlLoopThreadCompTest.java:194) 执行相关逻辑
[INFO] 2022-02-20 21:59:59.466 [可循环控制线程] (CtrlLoopThreadCompTest.java:194) 执行相关逻辑
[INFO] 2022-02-20 21:59:59.976 [可循环控制线程] (CtrlLoopThreadCompTest.java:194) 执行相关逻辑
[INFO] 2022-02-20 22:00:00.490 [可循环控制线程] (CtrlLoopThreadCompTest.java:194) 执行相关逻辑
[INFO] 2022-02-20 22:00:00.991 [可循环控制线程] (CtrlLoopThreadCompTest.java:194) 执行相关逻辑
[DEBUG] 2022-02-20 22:00:01.424 [可循环控制线程] (CtrlLoopThreadComp.java:501) CtrlLoopThread [可循环控制线程] was interrupted during sleep
[DEBUG] 2022-02-20 22:00:01.424 [可循环控制线程] (CtrlLoopThreadComp.java:506) CtrlLoopThread [可循环控制线程] end!!!

当然它的功能也不可能这么简单, 它的一个好用的地方就是下面的控制的过程

CtrlLoopThreadComp 线程控制方法

方法描述
pause()线程在调用完当前 loop 循环后, 暂停
pause(long)线程在调用完当前 loop 循环后, 暂停long毫秒后自动恢复, 其中使用的是 wait(long)
pauseAfterLoopTime(int)线程接下来执行int次 loop 循环后, 暂停.
wake()线程在暂停的状态下恢复 loop 循环调用
startOrWake()线程启动或恢复, 若是内置线程没有启动的情况下启动内置线程, 若是暂停状态下则对线程进行唤醒
close()线程结束当前 loop 循环之后, 线程关闭
start()内置线程启动, 直接调用的thread.start() 方法, 二次调用会抛出异常.
startIfNotStart()如果内置线程没有启动的时候启动, 可以多次调用, 但是只有第一次生效.

线程控制方法测试

/**
* 基础功能测试
*/
@Test
public void baseTest() {final CtrlLoopThreadComp threadComp = CtrlLoopThreadComp.ofRunnable(() -> {final String s = UUID.randomUUID().toString();log.info("-----------------------开始执行线程方法 : {}", s);// 睡眠 200 毫秒, 睡眠期间有异常则直接转换为 RuntimeException 抛出Throws.con(200, Thread::sleep).runtimeExp();log.info("-----------------------结束执行线程方法 : {}", s);})// 执行出现异常则继续下一次执行.catchFun(CtrlLoopThreadComp.CATCH_FUNCTION_CONTINUE)// 每 100 毫秒执行一次.setMillisecond(300)// 线程名称名为 COM-RUN.setName("可循环控制线程 1");// 启动线程log.info(" ======> 启动循环线程");threadComp.start();Throws.con(2000, Thread::sleep).runtimeExp();log.info(" ======> 暂停循环线程");threadComp.pause();Throws.con(2000, Thread::sleep).runtimeExp();log.info(" ======> 启动或唤醒循环线程, 1秒后再次暂停");threadComp.startOrWake();threadComp.pause(1000);Throws.con(2000, Thread::sleep).runtimeExp();log.info(" ======> 启动循环线程, 再执行 5 次循环后自动暂停");threadComp.pauseAfterLoopTime(5);Throws.con(5000, Thread::sleep).runtimeExp();log.info(" ======> 启动或唤醒循环线程, 2秒后 线程关闭");threadComp.startOrWake();Throws.con(2000, Thread::sleep).runtimeExp();threadComp.close();
}

运行结果

[INFO] 2022-02-20 22:09:39.257 [main] (CtrlLoopThreadCompTest.java:45)  ======> 启动循环线程
[DEBUG] 2022-02-20 22:09:39.267 [可循环控制线程 1] (CtrlLoopThreadComp.java:425) CtrlLoopThread [可循环控制线程 1] start!!!
[INFO] 2022-02-20 22:09:39.366 [可循环控制线程 1] (CtrlLoopThreadCompTest.java:33) -----------------------开始执行线程方法 : 17947299-b4e2-4c76-a25d-bdab500cde0a
[INFO] 2022-02-20 22:09:39.582 [可循环控制线程 1] (CtrlLoopThreadCompTest.java:36) -----------------------结束执行线程方法 : 17947299-b4e2-4c76-a25d-bdab500cde0a
[INFO] 2022-02-20 22:09:39.883 [可循环控制线程 1] (CtrlLoopThreadCompTest.java:33) -----------------------开始执行线程方法 : b1f9853e-d560-4430-8a1f-b2ab22f2891c
[INFO] 2022-02-20 22:09:40.098 [可循环控制线程 1] (CtrlLoopThreadCompTest.java:36) -----------------------结束执行线程方法 : b1f9853e-d560-4430-8a1f-b2ab22f2891c
[INFO] 2022-02-20 22:09:40.403 [可循环控制线程 1] (CtrlLoopThreadCompTest.java:33) -----------------------开始执行线程方法 : ca4b0fb6-863e-45e2-90e5-71960bdd24cf
[INFO] 2022-02-20 22:09:40.619 [可循环控制线程 1] (CtrlLoopThreadCompTest.java:36) -----------------------结束执行线程方法 : ca4b0fb6-863e-45e2-90e5-71960bdd24cf
[INFO] 2022-02-20 22:09:40.920 [可循环控制线程 1] (CtrlLoopThreadCompTest.java:33) -----------------------开始执行线程方法 : 3778424d-5ad2-44c3-b93b-7fda3657b964
[INFO] 2022-02-20 22:09:41.136 [可循环控制线程 1] (CtrlLoopThreadCompTest.java:36) -----------------------结束执行线程方法 : 3778424d-5ad2-44c3-b93b-7fda3657b964
[INFO] 2022-02-20 22:09:41.278 [main] (CtrlLoopThreadCompTest.java:49)  ======> 暂停循环线程
[DEBUG] 2022-02-20 22:09:41.441 [可循环控制线程 1] (CtrlLoopThreadComp.java:448) CtrlLoopThread [可循环控制线程 1] pause!!!
[INFO] 2022-02-20 22:09:43.293 [main] (CtrlLoopThreadCompTest.java:53)  ======> 启动或唤醒循环线程, 1秒后再次暂停
[DEBUG] 2022-02-20 22:09:43.293 [可循环控制线程 1] (CtrlLoopThreadComp.java:466) CtrlLoopThread [可循环控制线程 1] wake!!!
[DEBUG] 2022-02-20 22:09:43.293 [可循环控制线程 1] (CtrlLoopThreadComp.java:448) CtrlLoopThread [可循环控制线程 1] pause!!!
[DEBUG] 2022-02-20 22:09:44.308 [可循环控制线程 1] (CtrlLoopThreadComp.java:466) CtrlLoopThread [可循环控制线程 1] wake!!!
[INFO] 2022-02-20 22:09:44.308 [可循环控制线程 1] (CtrlLoopThreadCompTest.java:33) -----------------------开始执行线程方法 : ef85de83-f102-44b1-bc99-01b7a30962c6
[INFO] 2022-02-20 22:09:44.511 [可循环控制线程 1] (CtrlLoopThreadCompTest.java:36) -----------------------结束执行线程方法 : ef85de83-f102-44b1-bc99-01b7a30962c6
[INFO] 2022-02-20 22:09:44.828 [可循环控制线程 1] (CtrlLoopThreadCompTest.java:33) -----------------------开始执行线程方法 : f8b60de6-d2ab-43b5-9510-4a22bd1646c4
[INFO] 2022-02-20 22:09:45.028 [可循环控制线程 1] (CtrlLoopThreadCompTest.java:36) -----------------------结束执行线程方法 : f8b60de6-d2ab-43b5-9510-4a22bd1646c4
[INFO] 2022-02-20 22:09:45.307 [main] (CtrlLoopThreadCompTest.java:58)  ======> 启动循环线程, 再执行 5 次循环后自动暂停
[INFO] 2022-02-20 22:09:45.348 [可循环控制线程 1] (CtrlLoopThreadCompTest.java:33) -----------------------开始执行线程方法 : 7c55f307-1725-417a-a882-e2fd7f9ff6c2
[INFO] 2022-02-20 22:09:45.551 [可循环控制线程 1] (CtrlLoopThreadCompTest.java:36) -----------------------结束执行线程方法 : 7c55f307-1725-417a-a882-e2fd7f9ff6c2
[INFO] 2022-02-20 22:09:45.852 [可循环控制线程 1] (CtrlLoopThreadCompTest.java:33) -----------------------开始执行线程方法 : d1267012-930e-4d62-aac1-656ec014cbb7
[INFO] 2022-02-20 22:09:46.052 [可循环控制线程 1] (CtrlLoopThreadCompTest.java:36) -----------------------结束执行线程方法 : d1267012-930e-4d62-aac1-656ec014cbb7
[INFO] 2022-02-20 22:09:46.356 [可循环控制线程 1] (CtrlLoopThreadCompTest.java:33) -----------------------开始执行线程方法 : fdce9373-cb38-4fc7-8aef-9aa513147411
[INFO] 2022-02-20 22:09:46.563 [可循环控制线程 1] (CtrlLoopThreadCompTest.java:36) -----------------------结束执行线程方法 : fdce9373-cb38-4fc7-8aef-9aa513147411
[INFO] 2022-02-20 22:09:46.864 [可循环控制线程 1] (CtrlLoopThreadCompTest.java:33) -----------------------开始执行线程方法 : 7f13ee60-8415-4a40-ad88-217145561956
[INFO] 2022-02-20 22:09:47.064 [可循环控制线程 1] (CtrlLoopThreadCompTest.java:36) -----------------------结束执行线程方法 : 7f13ee60-8415-4a40-ad88-217145561956
[INFO] 2022-02-20 22:09:47.384 [可循环控制线程 1] (CtrlLoopThreadCompTest.java:33) -----------------------开始执行线程方法 : 24033216-67bd-43b7-ad96-7d602f90b87e
[INFO] 2022-02-20 22:09:47.591 [可循环控制线程 1] (CtrlLoopThreadCompTest.java:36) -----------------------结束执行线程方法 : 24033216-67bd-43b7-ad96-7d602f90b87e
[DEBUG] 2022-02-20 22:09:47.892 [可循环控制线程 1] (CtrlLoopThreadComp.java:448) CtrlLoopThread [可循环控制线程 1] pause!!!
[INFO] 2022-02-20 22:09:50.321 [main] (CtrlLoopThreadCompTest.java:62)  ======> 启动或唤醒循环线程, 2秒后 线程关闭
[DEBUG] 2022-02-20 22:09:50.321 [可循环控制线程 1] (CtrlLoopThreadComp.java:466) CtrlLoopThread [可循环控制线程 1] wake!!!
[INFO] 2022-02-20 22:09:50.321 [可循环控制线程 1] (CtrlLoopThreadCompTest.java:33) -----------------------开始执行线程方法 : f70f0969-a240-4208-abac-2a0dfd1fed8d
[INFO] 2022-02-20 22:09:50.521 [可循环控制线程 1] (CtrlLoopThreadCompTest.java:36) -----------------------结束执行线程方法 : f70f0969-a240-4208-abac-2a0dfd1fed8d
[INFO] 2022-02-20 22:09:50.822 [可循环控制线程 1] (CtrlLoopThreadCompTest.java:33) -----------------------开始执行线程方法 : f4908c34-f773-46f4-b56b-d451bb0afe3a
[INFO] 2022-02-20 22:09:51.023 [可循环控制线程 1] (CtrlLoopThreadCompTest.java:36) -----------------------结束执行线程方法 : f4908c34-f773-46f4-b56b-d451bb0afe3a
[INFO] 2022-02-20 22:09:51.341 [可循环控制线程 1] (CtrlLoopThreadCompTest.java:33) -----------------------开始执行线程方法 : bda3e0fc-f40b-4241-bc5d-6c578011956b
[INFO] 2022-02-20 22:09:51.548 [可循环控制线程 1] (CtrlLoopThreadCompTest.java:36) -----------------------结束执行线程方法 : bda3e0fc-f40b-4241-bc5d-6c578011956b
[INFO] 2022-02-20 22:09:51.849 [可循环控制线程 1] (CtrlLoopThreadCompTest.java:33) -----------------------开始执行线程方法 : 02d70f5c-be3e-446d-b61f-9bce3313275f
[INFO] 2022-02-20 22:09:52.049 [可循环控制线程 1] (CtrlLoopThreadCompTest.java:36) -----------------------结束执行线程方法 : 02d70f5c-be3e-446d-b61f-9bce3313275f
[DEBUG] 2022-02-20 22:09:52.326 [可循环控制线程 1] (CtrlLoopThreadComp.java:501) CtrlLoopThread [可循环控制线程 1] was interrupted during sleep
[DEBUG] 2022-02-20 22:09:52.326 [可循环控制线程 1] (CtrlLoopThreadComp.java:506) CtrlLoopThread [可循环控制线程 1] end!!!

CtrlLoopThreadComp 实现异步消息队列处理1

简单的异步处理队列

// 异步消息队列
private final BlockingQueue<String> blockingQueue = new LinkedBlockingQueue<>(1000);/**
* 异步消息处理测试
*/
@Test
public void asynchronousQueueTest() {// 异步处理器final CtrlLoopThreadComp threadComp = CtrlLoopThreadComp.ofRunnable(() -> {try {String str = blockingQueue.take();log.info("异步线程打印信息 : {}", str);} catch (InterruptedException e) {e.printStackTrace();}})// 执行出现异常则继续下一次执行.catchFun(CtrlLoopThreadComp.CATCH_FUNCTION_CONTINUE).setMillisecond(0).setName("异步处理线程");threadComp.start();// 即时往队列里面添加消息, 异步处理线程即时处理blockingQueue.add("消息1");blockingQueue.add("消息2");blockingQueue.add("消息3");blockingQueue.add("消息4");blockingQueue.add("消息5");// 主线程睡眠 10 毫秒, 方便队列消息能够处理完成Throws.con(10, Thread::sleep).logThrowable();
}

打印日志

[INFO] 2022-02-20 22:17:55.424 [异步处理线程] (CtrlLoopThreadCompTest.java:189) 异步线程打印信息 : 消息1
[INFO] 2022-02-20 22:17:55.424 [异步处理线程] (CtrlLoopThreadCompTest.java:189) 异步线程打印信息 : 消息2
[INFO] 2022-02-20 22:17:55.424 [异步处理线程] (CtrlLoopThreadCompTest.java:189) 异步线程打印信息 : 消息3
[INFO] 2022-02-20 22:17:55.424 [异步处理线程] (CtrlLoopThreadCompTest.java:189) 异步线程打印信息 : 消息4
[INFO] 2022-02-20 22:17:55.424 [异步处理线程] (CtrlLoopThreadCompTest.java:189) 异步线程打印信息 : 消息5

CtrlLoopThreadComp 实现异步消息队列处理2

实现先异步处理6条消息, 之后暂停, 再唤醒

// 异步消息队列
private final BlockingQueue<String> blockingQueue = new LinkedBlockingQueue<>(1000);/**
* 异步消息处理测试2
*/
@Test
public void asynchronousQueueTest2() {// 异步处理器final CtrlLoopThreadComp threadComp = CtrlLoopThreadComp.ofRunnable(() -> {try {String str = blockingQueue.take();log.info("异步线程打印信息 : {}", str);} catch (InterruptedException e) {e.printStackTrace();}})// 执行出现异常则继续下一次执行.catchFun(CtrlLoopThreadComp.CATCH_FUNCTION_CONTINUE).setMillisecond(0).setName("异步处理线程");// 处理 6 条消息后暂停threadComp.pauseAfterLoopTime(6);threadComp.start();// 即时往队列里面添加消息, 异步处理线程即时处理blockingQueue.add("消息1");blockingQueue.add("消息2");blockingQueue.add("消息3");blockingQueue.add("消息4");blockingQueue.add("消息5");blockingQueue.add("消息6");blockingQueue.add("消息7");blockingQueue.add("消息8");blockingQueue.add("消息9");blockingQueue.add("消息10");Throws.con(2000, Thread::sleep).logThrowable();log.info("发现继续添加消息,不再进行处理");log.info("启动线程后继续处理消息");threadComp.wake();Throws.con(10, Thread::sleep).logThrowable();
}

打印日志

[INFO] 2022-02-20 22:21:45.235 [异步处理线程] (CtrlLoopThreadCompTest.java:221) 异步线程打印信息 : 消息1
[INFO] 2022-02-20 22:21:45.235 [异步处理线程] (CtrlLoopThreadCompTest.java:221) 异步线程打印信息 : 消息2
[INFO] 2022-02-20 22:21:45.235 [异步处理线程] (CtrlLoopThreadCompTest.java:221) 异步线程打印信息 : 消息3
[INFO] 2022-02-20 22:21:45.235 [异步处理线程] (CtrlLoopThreadCompTest.java:221) 异步线程打印信息 : 消息4
[INFO] 2022-02-20 22:21:45.235 [异步处理线程] (CtrlLoopThreadCompTest.java:221) 异步线程打印信息 : 消息5
[INFO] 2022-02-20 22:21:45.235 [异步处理线程] (CtrlLoopThreadCompTest.java:221) 异步线程打印信息 : 消息6
[DEBUG] 2022-02-20 22:21:45.235 [异步处理线程] (CtrlLoopThreadComp.java:448) CtrlLoopThread [异步处理线程] pause!!!
[INFO] 2022-02-20 22:21:47.239 [main] (CtrlLoopThreadCompTest.java:248) 发现继续添加消息,不再进行处理
[INFO] 2022-02-20 22:21:47.239 [main] (CtrlLoopThreadCompTest.java:250) 启动线程后继续处理消息
[DEBUG] 2022-02-20 22:21:47.239 [异步处理线程] (CtrlLoopThreadComp.java:466) CtrlLoopThread [异步处理线程] wake!!!
[INFO] 2022-02-20 22:21:47.239 [异步处理线程] (CtrlLoopThreadCompTest.java:221) 异步线程打印信息 : 消息7
[INFO] 2022-02-20 22:21:47.239 [异步处理线程] (CtrlLoopThreadCompTest.java:221) 异步线程打印信息 : 消息8
[INFO] 2022-02-20 22:21:47.239 [异步处理线程] (CtrlLoopThreadCompTest.java:221) 异步线程打印信息 : 消息9
[INFO] 2022-02-20 22:21:47.239 [异步处理线程] (CtrlLoopThreadCompTest.java:221) 异步线程打印信息 : 消息10

并发测试

pause(), startOrWake(), .wake()等方法都是可以在多线程之中重复调用的.

并发调用 pause(), startOrWake(), .wake(), 看下执行逻辑是否出错

代码如下

/**
* 并发调用几个方法, 看下执行逻辑是否出错
*/
@Test
public void ConcurrentTest() throws InterruptedException {final CtrlLoopThreadComp threadComp = CtrlLoopThreadComp.ofRunnable(() -> {final String s = UUID.randomUUID().toString();log.info("-----------------------开始执行线程方法 : {}", s);// 睡眠 200 毫秒, 睡眠期间有异常则直接转换为 RuntimeException 抛出Throws.con(RandomUtils.nextInt(0, 3), Thread::sleep).runtimeExp();log.info("-----------------------结束执行线程方法 : {}", s);})// 执行出现异常则继续下一次执行.catchFun(CtrlLoopThreadComp.CATCH_FUNCTION_CONTINUE)// 每 100 毫秒执行一次.setMillisecond(100)// 线程名称名为 COM-RUN.setName("可循环控制线程 2");// 启动线程log.info(" ======> 启动线程");threadComp.start();final int loop = 100000;CountDownLatch countDownLatch = new CountDownLatch(loop);IntStream.range(0, loop).parallel().forEach(num -> {final int i = RandomUtils.nextInt(0, 5);switch (i) {case 0:threadComp.pause();break;case 1:threadComp.startOrWake();break;case 2:threadComp.pause(1);break;case 3:threadComp.pause();Throws.con(1, Thread::sleep).runtimeExp();threadComp.wake();break;case 4:threadComp.startIfNotStart();break;default:}countDownLatch.countDown();});countDownLatch.await();log.info(" ======> 执行完毕关闭线程");threadComp.close();threadComp.close();
}

执行日志如下

  • 100000 次并发, 执行逻辑并没有出错
  • 注意: 由于日志较多, 删除了一些日志.
[INFO] 2022-02-20 22:26:35.150 [main] (CtrlLoopThreadCompTest.java:87)  ======> 启动线程
[DEBUG] 2022-02-20 22:26:35.150 [可循环控制线程 2] (CtrlLoopThreadComp.java:425) CtrlLoopThread [可循环控制线程 2] start!!!
[INFO] 2022-02-20 22:26:35.273 [可循环控制线程 2] (CtrlLoopThreadCompTest.java:75) -----------------------开始执行线程方法 : 480d8b3c-80ba-4a8e-bce3-17debf5401d7
[INFO] 2022-02-20 22:26:35.276 [可循环控制线程 2] (CtrlLoopThreadCompTest.java:78) -----------------------结束执行线程方法 : 480d8b3c-80ba-4a8e-bce3-17debf5401d7
[DEBUG] 2022-02-20 22:26:35.377 [可循环控制线程 2] (CtrlLoopThreadComp.java:448) CtrlLoopThread [可循环控制线程 2] pause!!!
[DEBUG] 2022-02-20 22:26:35.378 [可循环控制线程 2] (CtrlLoopThreadComp.java:466) CtrlLoopThread [可循环控制线程 2] wake!!!
[INFO] 2022-02-20 22:26:35.378 [可循环控制线程 2] (CtrlLoopThreadCompTest.java:75) -----------------------开始执行线程方法 : 3d4e9b6b-6812-4ac0-b860-a4bb373b1769
[INFO] 2022-02-20 22:26:35.381 [可循环控制线程 2] (CtrlLoopThreadCompTest.java:78) -----------------------结束执行线程方法 : 3d4e9b6b-6812-4ac0-b860-a4bb373b1769
[DEBUG] 2022-02-20 22:26:35.481 [可循环控制线程 2] (CtrlLoopThreadComp.java:448) CtrlLoopThread [可循环控制线程 2] pause!!!
[DEBUG] 2022-02-20 22:26:35.482 [可循环控制线程 2] (CtrlLoopThreadComp.java:466) CtrlLoopThread [可循环控制线程 2] wake!!!
[INFO] 2022-02-20 22:26:35.587 [可循环控制线程 2] (CtrlLoopThreadCompTest.java:75) -----------------------开始执行线程方法 : a0eaf9dd-3c38-492d-9651-969395572a03
[INFO] 2022-02-20 22:26:35.590 [可循环控制线程 2] (CtrlLoopThreadCompTest.java:78) -----------------------结束执行线程方法 : a0eaf9dd-3c38-492d-9651-969395572a03
[DEBUG] 2022-02-20 22:26:35.691 [可循环控制线程 2] (CtrlLoopThreadComp.java:448) CtrlLoopThread [可循环控制线程 2] pause!!!
[DEBUG] 2022-02-20 22:26:35.692 [可循环控制线程 2] (CtrlLoopThreadComp.java:466) CtrlLoopThread [可循环控制线程 2] wake!!!
[INFO] 2022-02-20 22:26:35.696 [可循环控制线程 2] (CtrlLoopThreadCompTest.java:75) -----------------------开始执行线程方法 : 98a8e1c2-c666-489e-a1a3-77081be2fc97
[INFO] 2022-02-20 22:26:35.696 [可循环控制线程 2] (CtrlLoopThreadCompTest.java:78) -----------------------结束执行线程方法 : 98a8e1c2-c666-489e-a1a3-77081be2fc97
[INFO] 2022-02-20 22:26:36.106 [可循环控制线程 2] (CtrlLoopThreadCompTest.java:75) -----------------------开始执行线程方法 : c578f927-4b28-4623-80ae-414fafcf3fab
[INFO] 2022-02-20 22:26:36.108 [可循环控制线程 2] (CtrlLoopThreadCompTest.java:78) -----------------------结束执行线程方法 : c578f927-4b28-4623-80ae-414fafcf3fab
[DEBUG] 2022-02-20 22:26:36.209 [可循环控制线程 2] (CtrlLoopThreadComp.java:448) CtrlLoopThread [可循环控制线程 2] pause!!!
[DEBUG] 2022-02-20 22:26:36.210 [可循环控制线程 2] (CtrlLoopThreadComp.java:466) CtrlLoopThread [可循环控制线程 2] wake!!!
[DEBUG] 2022-02-20 22:26:36.210 [可循环控制线程 2] (CtrlLoopThreadComp.java:448) CtrlLoopThread [可循环控制线程 2] pause!!!
[DEBUG] 2022-02-20 22:26:36.210 [可循环控制线程 2] (CtrlLoopThreadComp.java:466) CtrlLoopThread [可循环控制线程 2] wake!!!
[DEBUG] 2022-02-20 22:26:36.210 [可循环控制线程 2] (CtrlLoopThreadComp.java:448) CtrlLoopThread [可循环控制线程 2] pause!!!
[DEBUG] 2022-02-20 22:26:36.212 [可循环控制线程 2] (CtrlLoopThreadComp.java:466) CtrlLoopThread [可循环控制线程 2] wake!!!
[INFO] 2022-02-20 22:26:36.212 [可循环控制线程 2] (CtrlLoopThreadCompTest.java:75) -----------------------开始执行线程方法 : fafbbd4b-398b-4531-986e-95fd665def07
[INFO] 2022-02-20 22:26:36.215 [可循环控制线程 2] (CtrlLoopThreadCompTest.java:78) -----------------------结束执行线程方法 : fafbbd4b-398b-4531-986e-95fd665def07
[DEBUG] 2022-02-20 22:26:36.316 [可循环控制线程 2] (CtrlLoopThreadComp.java:448) CtrlLoopThread [可循环控制线程 2] pause!!!
[DEBUG] 2022-02-20 22:26:36.317 [可循环控制线程 2] (CtrlLoopThreadComp.java:466) CtrlLoopThread [可循环控制线程 2] wake!!!
[INFO] 2022-02-20 22:26:36.317 [可循环控制线程 2] (CtrlLoopThreadCompTest.java:75) -----------------------开始执行线程方法 : af5269ce-a1d0-4c3b-961e-432f9a5b2579
[INFO] 2022-02-20 22:26:36.319 [可循环控制线程 2] (CtrlLoopThreadCompTest.java:78) -----------------------结束执行线程方法 : af5269ce-a1d0-4c3b-961e-432f9a5b2579
[DEBUG] 2022-02-20 22:26:36.419 [可循环控制线程 2] (CtrlLoopThreadComp.java:448) CtrlLoopThread [可循环控制线程 2] pause!!!
[DEBUG] 2022-02-20 22:26:36.420 [可循环控制线程 2] (CtrlLoopThreadComp.java:466) CtrlLoopThread [可循环控制线程 2] wake!!!
[INFO] 2022-02-20 22:26:36.420 [可循环控制线程 2] (CtrlLoopThreadCompTest.java:75) -----------------------开始执行线程方法 : 7bfbd960-7693-40d9-9786-f63a5bdc8bd5
[INFO] 2022-02-20 22:26:36.423 [可循环控制线程 2] (CtrlLoopThreadCompTest.java:78) -----------------------结束执行线程方法 : 7bfbd960-7693-40d9-9786-f63a5bdc8bd5
[DEBUG] 2022-02-20 22:26:36.524 [可循环控制线程 2] (CtrlLoopThreadComp.java:448) CtrlLoopThread [可循环控制线程 2] pause!!!
[DEBUG] 2022-02-20 22:26:36.525 [可循环控制线程 2] (CtrlLoopThreadComp.java:466) CtrlLoopThread [可循环控制线程 2] wake!!!
[DEBUG] 2022-02-20 22:26:37.059 [可循环控制线程 2] (CtrlLoopThreadComp.java:448) CtrlLoopThread [可循环控制线程 2] pause!!!
[DEBUG] 2022-02-20 22:26:37.060 [可循环控制线程 2] (CtrlLoopThreadComp.java:466) CtrlLoopThread [可循环控制线程 2] wake!!!
[INFO] 2022-02-20 22:26:37.066 [可循环控制线程 2] (CtrlLoopThreadCompTest.java:75) -----------------------开始执行线程方法 : 40a19c65-5eda-4387-95cf-6b6c1fc65d32
[INFO] 2022-02-20 22:26:37.068 [可循环控制线程 2] (CtrlLoopThreadCompTest.java:78) -----------------------结束执行线程方法 : 40a19c65-5eda-4387-95cf-6b6c1fc65d32
[DEBUG] 2022-02-20 22:26:37.169 [可循环控制线程 2] (CtrlLoopThreadComp.java:448) CtrlLoopThread [可循环控制线程 2] pause!!!
[DEBUG] 2022-02-20 22:26:37.170 [可循环控制线程 2] (CtrlLoopThreadComp.java:466) CtrlLoopThread [可循环控制线程 2] wake!!!
[INFO] 2022-02-20 22:26:37.172 [可循环控制线程 2] (CtrlLoopThreadCompTest.java:75) -----------------------开始执行线程方法 : c4882072-43a9-4c4a-8c28-16b05a04ad33
[INFO] 2022-02-20 22:26:37.174 [可循环控制线程 2] (CtrlLoopThreadCompTest.java:78) -----------------------结束执行线程方法 : c4882072-43a9-4c4a-8c28-16b05a04ad33
[DEBUG] 2022-02-20 22:26:37.274 [可循环控制线程 2] (CtrlLoopThreadComp.java:448) CtrlLoopThread [可循环控制线程 2] pause!!!
[DEBUG] 2022-02-20 22:26:37.275 [可循环控制线程 2] (CtrlLoopThreadComp.java:466) CtrlLoopThread [可循环控制线程 2] wake!!!
[INFO] 2022-02-20 22:26:37.277 [可循环控制线程 2] (CtrlLoopThreadCompTest.java:75) -----------------------开始执行线程方法 : e3ae9eae-8f88-4834-9e3a-fad9708a80d0
[INFO] 2022-02-20 22:26:37.279 [可循环控制线程 2] (CtrlLoopThreadCompTest.java:78) -----------------------结束执行线程方法 : e3ae9eae-8f88-4834-9e3a-fad9708a80d0
[DEBUG] 2022-02-20 22:26:37.380 [可循环控制线程 2] (CtrlLoopThreadComp.java:448) CtrlLoopThread [可循环控制线程 2] pause!!!
[DEBUG] 2022-02-20 22:26:37.595 [可循环控制线程 2] (CtrlLoopThreadComp.java:466) CtrlLoopThread [可循环控制线程 2] wake!!!
[DEBUG] 2022-02-20 22:26:37.595 [可循环控制线程 2] (CtrlLoopThreadComp.java:448) CtrlLoopThread [可循环控制线程 2] pause!!!
[DEBUG] 2022-02-20 22:26:37.595 [可循环控制线程 2] (CtrlLoopThreadComp.java:466) CtrlLoopThread [可循环控制线程 2] wake!!!
[INFO] 2022-02-20 22:26:37.595 [可循环控制线程 2] (CtrlLoopThreadCompTest.java:75) -----------------------开始执行线程方法 : 7a12ef36-3a99-450d-a370-e9b00ff9bf70
[INFO] 2022-02-20 22:26:37.596 [可循环控制线程 2] (CtrlLoopThreadCompTest.java:78) -----------------------结束执行线程方法 : 7a12ef36-3a99-450d-a370-e9b00ff9bf70
[INFO] 2022-02-20 22:26:37.625 [main] (CtrlLoopThreadCompTest.java:118)  ======> 执行完毕关闭线程
[DEBUG] 2022-02-20 22:26:37.625 [可循环控制线程 2] (CtrlLoopThreadComp.java:501) CtrlLoopThread [可循环控制线程 2] was interrupted during sleep
[DEBUG] 2022-02-20 22:26:37.625 [可循环控制线程 2] (CtrlLoopThreadComp.java:506) CtrlLoopThread [可循环控制线程 2] end!!!

除此之外, 还有一些功能

首先,CtrlLoopThreadComp.ofSupplier() 方法可以传入一个 Supplier 作为loop循环.

在循环中, 若是 Supplier 返回true, 则表示正常执行, 若是返回 false, 则会调用 falseFun 传入的 Consumer<CtrlComp> falseConsumer 方法.

若是在循环中执行报错, 则会调用 catchFun() 传入的BiConsumer<CtrlComp, RuntimeException> catchConsumer方法.

在这两个方法里面, 均有一个 CtrlComp 对象用于控制内置线程的相关状态.

使用方式如下.

@Test
public void asynchronousQueueTest3() {// 异步处理器final CtrlLoopThreadComp threadComp = CtrlLoopThreadComp.ofSupplier(() -> {String str = "";try {str = blockingQueue.take();} catch (InterruptedException e) {e.printStackTrace();}log.info("成功接收到了字符串 ==> {}", str);// 字符串转数字,转换不成功会抛出异常final int i = Integer.parseInt(str);// 偶数返回 true,奇数返回falsereturn i % 2 == 0;}).setMillisecond(0).setName("异步处理线程")// 执行出现异常则继续下一次执行.catchFun((ctrlComp, e) -> {log.error("执行异常的时候发生了错误 : {}", e.getMessage());// 执行错误后,暂停 300 毫秒ctrlComp.pause(300);// 执行错误后,结束循环线程// ctrlComp.endCtrlLoopThread();}).falseFun(ctrlComp -> {log.warn("执行 loop 循环的之后返回了 false");// 继续执行ctrlComp.continueNextLoop();});threadComp.start();// 即时往队列里面添加消息, 异步处理线程即时处理blockingQueue.add("23");blockingQueue.add("11");blockingQueue.add("32");blockingQueue.add("0");blockingQueue.add("哈哈");Throws.con(10, Thread::sleep).logThrowable();
}

执行日志

[INFO] 2022-02-20 22:48:59.137 [异步处理线程] (CtrlLoopThreadCompTest.java:272) 成功接收到了字符串 ==> 23
[WARN] 2022-02-20 22:48:59.137 [异步处理线程] (CtrlLoopThreadCompTest.java:289) 执行 loop 循环的之后返回了 false
[INFO] 2022-02-20 22:48:59.137 [异步处理线程] (CtrlLoopThreadCompTest.java:272) 成功接收到了字符串 ==> 11
[WARN] 2022-02-20 22:48:59.137 [异步处理线程] (CtrlLoopThreadCompTest.java:289) 执行 loop 循环的之后返回了 false
[INFO] 2022-02-20 22:48:59.137 [异步处理线程] (CtrlLoopThreadCompTest.java:272) 成功接收到了字符串 ==> 32
[INFO] 2022-02-20 22:48:59.137 [异步处理线程] (CtrlLoopThreadCompTest.java:272) 成功接收到了字符串 ==> 0
[INFO] 2022-02-20 22:48:59.137 [异步处理线程] (CtrlLoopThreadCompTest.java:272) 成功接收到了字符串 ==> 哈哈
[ERROR] 2022-02-20 22:48:59.137 [异步处理线程] (CtrlLoopThreadCompTest.java:282) 执行异常的时候发生了错误 : For input string: "哈哈"
[DEBUG] 2022-02-20 22:48:59.137 [异步处理线程] (CtrlLoopThreadComp.java:448) CtrlLoopThread [异步处理线程] pause!!!

CtrlLoopThreadComp 源码

就这一个类, 在开发的时候主键加入功能, 前前后后真的是更改了好久.

package com.github.cosycode.common.thread;import lombok.Setter;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;import java.util.function.BiConsumer;
import java.util.function.BooleanSupplier;
import java.util.function.Consumer;/*** <b>Description : </b>  可控制的单循环线程, 和 CtrlLoopThread 的区别是对 Thread 采用了组合方式, 而不是继承方式* <p>* <b>设计如下: </b>* <br> <b>线程终止: </b> 只要内置线程调用 interrupt() 方法即视为线程需要终止.* <br> <b>线程等待和唤醒机制: </b> 因为线程调用 interrupt() 方法视为线程需要终止, 因此此处使用 wait + notify 来管理线程等待和唤醒.* </p>* <b>created in </b> 2020/8/13** @author CPF* @since 1.0*/
@Slf4j
@Accessors(chain = true)
public class CtrlLoopThreadComp implements AutoCloseable {/*** 抛出异常调用方法: 打印现场后继续下一次循环*/public static final BiConsumer<CtrlComp, RuntimeException> CATCH_FUNCTION_CONTINUE = CtrlComp::logException;/*** 参照 Thread 中为每个线程生成一个 Id 的方法, 简单移植过来的.* 如果该类, 没有赋值 name, 则根据当前 Number 生成一个 name*/private static int threadInitNumber;/*** 内置线程对象*/protected final Thread thread;/*** 线程每次执行的函数, 如果函数返回false, 则线程循环结束*/private final BooleanSupplier booleanSupplier;/*** 多长时间运行一次(while true 中的一个执行sleep多久)*/@Setterprivate int millisecond;/*** 运行类*/private final CtrlLoopRunnable ctrlLoopRunnable;/*** 处理函数对象, 此处使用 volatile 仅仅保证该引用的可见性*/@SuppressWarnings("java:S3077")private volatile CtrlComp ctrlComp;/*** 返回 false 时调用方法*/private Consumer<CtrlComp> falseConsumer;/*** 出错时的消费函数接口*/private BiConsumer<CtrlComp, RuntimeException> catchConsumer;/*** @param booleanSupplier     运行函数, 如果运行中返回 false, 则暂停运行.* @param name                线程名称, 若为 empty, 则会自动取一个名字(以 CtrlLoopThreadComp- 开头)* @param continueIfException 发生异常时是否继续.* @param millisecond         两次循环之间间隔多久(毫秒)*/protected CtrlLoopThreadComp(BooleanSupplier booleanSupplier, String name, boolean continueIfException, int millisecond) {this(booleanSupplier, null, continueIfException ? CATCH_FUNCTION_CONTINUE : null, name, millisecond);}/*** CtrlLoopThreadComp 主要构造方法* <p>* <br> CtrlLoopThreadComp 里面内置了一个线程, 线程会 每隔 millisecond 毫秒 循环调用 booleanSupplier 方法, 若 millisecond <= 0, 则表示不进行暂停.* <br> 当调用 booleanSupplier 结果返回 true, 则隔 millisecond 毫秒后继续调用 booleanSupplier 方法* <br> 当调用 booleanSupplier 结果返回 false, 则调用 falseConsumer 方法, 若 falseConsumer 为 null, 则不对返回值做任何处理, 继续下一次循环* <br> 当调用 booleanSupplier 时抛出运行时异常, 则调用 catchConsumer 方法, 若 catchConsumer 为 null, 则不对异常做任何处理, 等于将异常抛给虚拟机.* </p>** @param booleanSupplier 运行函数(不可为 null)* @param falseConsumer   booleanSupplier 运行后返回 false 时调用函数* @param catchConsumer   booleanSupplier 运行后抛出 异常时 调用该函数* @param name            线程名称, 若为 empty, 则会自动取一个名字(以 CtrlLoopThreadComp- 开头)* @param millisecond     两次循环之间间隔多久(毫秒), 如果为 0 则表示不暂停.*/protected CtrlLoopThreadComp(BooleanSupplier booleanSupplier, Consumer<CtrlComp> falseConsumer, BiConsumer<CtrlComp, RuntimeException> catchConsumer, String name, int millisecond) {this.booleanSupplier = booleanSupplier;this.falseConsumer = falseConsumer;this.catchConsumer = catchConsumer;this.millisecond = millisecond;this.ctrlLoopRunnable = new CtrlLoopRunnable();this.thread = new Thread(this.ctrlLoopRunnable, StringUtils.isBlank(name) ? "CtrlLoopThreadComp-" + nextThreadNum() : name);}/*** 参照 Thread 中为每个线程生成一个 Id 的方法, 简单移植过来的.** @return 一个 Id 编号*/private static synchronized int nextThreadNum() {return threadInitNumber++;}/*** @param runnable            运行函数* @param continueIfException 发生异常时是否继续.* @param millisecond         两次循环之间间隔多久(毫秒)* @return 创建的线程*/public static CtrlLoopThreadComp ofRunnable(Runnable runnable, boolean continueIfException, int millisecond) {return new CtrlLoopThreadComp(() -> {runnable.run();return true;}, null, continueIfException, millisecond);}/*** @param runnable 运行函数* @return 创建的线程*/public static CtrlLoopThreadComp ofRunnable(Runnable runnable) {return new CtrlLoopThreadComp(() -> {runnable.run();return true;}, null, false, 0);}/*** @param booleanSupplier     运行函数, 如果运行中返回 false, 则暂停运行.* @param continueIfException 发生异常时是否继续.* @param millisecond         两次循环之间间隔多久(毫秒)* @return 创建的线程*/public static CtrlLoopThreadComp ofSupplier(BooleanSupplier booleanSupplier, boolean continueIfException, int millisecond) {return new CtrlLoopThreadComp(booleanSupplier, null, continueIfException, millisecond);}/*** @param booleanSupplier 运行函数, 如果运行中返回 false, 则暂停运行.* @return 创建的线程*/public static CtrlLoopThreadComp ofSupplier(BooleanSupplier booleanSupplier) {return new CtrlLoopThreadComp(booleanSupplier, null, false, 0);}/*** loop函数, 添加一个函数, 方便子类继承** @return 当前函数是否运行成功*/protected boolean loop() {return booleanSupplier != null && booleanSupplier.getAsBoolean();}/*** 线程暂停*/public void pause() {ctrlLoopRunnable.changeState(3, 0, 0);}/*** 线程暂停指定毫秒, 同Object.wait()一样, 若等待时间为0,则表示永久暂停。* 当线程正在暂停中时, 再次调用该方法, 线程在自动结束等待情况下, 将继续 wait 指定的时间** @param waitTime 暂停的时间(毫秒), 若为0,则表示永久暂停。*/public void pause(long waitTime) {ctrlLoopRunnable.changeState(3, waitTime, 0);}/*** 多少次运行之后暂停.** @param loopTime 次数*/public void pauseAfterLoopTime(int loopTime) {ctrlLoopRunnable.changeState(2, 0, loopTime);}/*** 线程恢复*/public void wake() {ctrlLoopRunnable.changeState(1, 0, 0);}/*** 内置线程启动*/public void start() {thread.start();}/*** 若没有启动的话, 则启动*/public synchronized void startIfNotStart() {if (Thread.State.NEW == thread.getState()) {thread.start();}}/*** @return 内置线程状态*/public Thread.State getThreadState() {return thread.getState();}/*** 线程启动或恢复*/public void startOrWake() {final Thread.State state = thread.getState();switch (state) {case NEW:startIfNotStart();break;case BLOCKED:case RUNNABLE:case WAITING:case TIMED_WAITING:wake();break;case TERMINATED:log.warn("wrong invocation! CtrlLoopThread [{}] has ended!!!", thread.getName());break;default:}}/*** 通过 interrupt 停止循环线程, 线程将会在执行完当前循环之后, 自动停止*/@Overridepublic void close() {ctrlLoopRunnable.closeWhenThisLoopEnd();}/*** 当 loop 循环返回 false 时调用该线程.** @param falseConsumer 返回 false 消费线程* @return 当前对象本身*/public CtrlLoopThreadComp falseFun(Consumer<CtrlComp> falseConsumer) {this.falseConsumer = falseConsumer;return this;}/*** 设置出错线程, 如果发生异常, 则会调用该方法** @param catchConsumer 出错消费线程* @return 当前对象本身*/public CtrlLoopThreadComp catchFun(BiConsumer<CtrlComp, RuntimeException> catchConsumer) {this.catchConsumer = catchConsumer;return this;}/*** 获取线程名称** @return 当前对象内线程名称*/public String getName() {return thread.getName();}/*** 设置线程名称** @param name 线程名称* @return 当前对象本身*/public CtrlLoopThreadComp setName(String name) {thread.setName(name);return this;}/*** @return 返回唯一的控制器*/public CtrlComp getCtrlComp() {if (ctrlComp == null) {synchronized (this) {if (ctrlComp == null) {ctrlComp = new CtrlComp();}}}return ctrlComp;}/*** @param continueIfException 发生异常后是否继续下一次循环* @return 对象本身*/public CtrlLoopThreadComp setContinueIfException(boolean continueIfException) {this.catchConsumer = CATCH_FUNCTION_CONTINUE;return this;}/*** <b>Description : </b> 可控制的循环线程的runnable内部类, 控制CtrlLoopThreadComp的执行方式* <p>* <b>created in </b> 2020/8/13** @author CPF* @since 1.0**/private class CtrlLoopRunnable implements Runnable {/*** 用于线程启停的锁*/private final Object lock = new Object();/*** 添加了线程状态 state* <p>* <br>添加 state 的好处是 使得更改 state 状态时变得容易理解, 最主要的目的就是方法 {@link CtrlLoopRunnable#changeState(int, long, int)}* <br>* <br> <b>0: </b>初始状态, 表示 state 还未被修改* <br> <b>1: </b>持续运行状态* <br> <b>2: </b>临时运行状态, 指定次数后转换为暂停状态, 此时 waitAfterLoopCount 有意义; 若 waitAfterLoopCount > 0, 则指定次数后转换为永久暂停状态, 否则马上转为永久暂停状态* <br> <b>3: </b>临时运行状态, 即将被暂停, 此时 waitTime 有意义; 若 waitTime>0, 则转为临时暂停状态, 否则转为永久暂停状态.* <br> <b>4: </b>临时暂停状态, 指定时间后被唤醒* <br> <b>5: </b>永久暂停状态, 需要使用 notify 唤醒* <br> <b>-1: </b>终止状态, 此时修改 state 已经没有意义*/private volatile int state;/*** 线程结束标记, 若该标记为 true, 则运行完当前 loop 则直接结束线程.*/private volatile boolean endFlag;/*** 线程正处在 loop 方法循环里面*/private volatile int codeRunLocation;/*** 等待时间, 这里使用 wait 和 notify 对线程进行唤醒*/private long waitTime;/*** 在多少次循环后暂停标记* <br> <b>-1: </b> 持续运行标记* <br> <b>0: </b> 运行至检查点, 触发暂停事件, 该值转为 -1* <br> <b>n(>0): </b> 运行至检查点, 该值减1*/@Setterprivate int waitAfterLoopCount;/*** 执行当前 loop 结束后, 关闭线程.* <p>* 如果在 loop 中, 则通过更改标记位, 结束线程.* 如果不在 loop 中, 而是在线程循环控制处理逻辑内, 则直接调用 interrupt 关闭线程.*/protected void closeWhenThisLoopEnd() {synchronized (lock) {this.endFlag = true;// 如果不是在 loop 的处理中, 则直接使用 interrupt 关闭线程if (codeRunLocation < 3 || codeRunLocation >= 5) {thread.interrupt();}}}/*** <br> <b>-2: </b> 运行完这一次直接关闭线程* <br> <b>1: </b>持续运行状态* <br> <b>2: </b>临时运行状态, 指定次数后转换为暂停状态, 此时 waitAfterLoopCount 有意义* <br> <b>3: </b>临时运行状态, 即将被暂停, 此时 waitTime 有意义** @param state             {@link CtrlLoopRunnable#state}* @param waitTime          等待时间, state=3时有意义, 若 waitTime>0, 则转为临时暂停状态, 否则转为永久暂停状态.* @param waitAfterLoopTime 循环指定次数后暂停, state=2时有意义, 若 waitAfterLoopCount > 0, 则指定次数后转换为永久暂停状态, 否则马上转为永久暂停状态*/protected void changeState(final int state, final long waitTime, final int waitAfterLoopTime) {synchronized (lock) {switch (state) {case 1:this.waitTime = 0;this.waitAfterLoopCount = 0;lock.notifyAll();break;case 2:this.waitTime = 0;if (codeRunLocation == 2) {// 此时运行在执行自定义函数之前, 次数判定之后, 因此此时需要将次数 - 1this.waitAfterLoopCount = waitAfterLoopTime - 1;} else {this.waitAfterLoopCount = waitAfterLoopTime;}lock.notifyAll();break;case 3:this.waitTime = waitTime;this.waitAfterLoopCount = 0;lock.notifyAll();break;default:}this.state = state;}}@Override@SuppressWarnings({"java:S1119", "java:S112"})public void run() {// 线程在启动之前, 可以不是0if (state == 0) {state = 1;}final String name = thread.getName();log.debug("CtrlLoopThread [{}] start!!!", name);outer:while (!thread.isInterrupted()) {// 临时运行状态, 指定次数后转换为暂停状态, 此时 waitAfterLoopCount 有意义if (state == 2) {synchronized (lock) {if (state == 2) {if (waitAfterLoopCount > 0) {waitAfterLoopCount--;} else {state = 3;waitTime = 0;}}}}codeRunLocation = 2;/* 这个地方使用额外的对象锁停止线程, 而不是使用线程本身的停滞机制, 保证一次循环执行完毕后执行停止操作, 而不是一次循环正在执行 booleanSupplier 的时候停止*/// 临时运行状态, 即将被暂停, 此时 waitTime 有意义, waitTime=0, 则转为永久暂停状态, waitTime>0, 则转为临时暂停状态if (state == 3) {synchronized (lock) {// 此处之所以使用 while 而不是 if 是因为想要在线程等待过程中或者结束后, 依然可以通过控制 state 使得线程可以再次陷入等待, 以及可以最佳等待时间.while (state == 3) {log.debug("CtrlLoopThread [{}] pause!!!", name);try {// 添加添加临时变量防止幻读final long waitTmp = waitTime;if (waitTmp > 0) {waitTime = 0;state = 4;lock.wait(waitTmp);} else {state = 5;lock.wait();}} catch (InterruptedException e) {log.debug("CtrlLoopThread [{}] was interrupted during waiting!!!", name);thread.interrupt();/* 线程中断即意味着线程结束, 此时跳出最外层循环 */break outer;}log.debug("CtrlLoopThread [{}] wake!!!", name);}}}codeRunLocation = 3;// 线程关闭标记为true, 则关闭线程if (endFlag) {break;}/* 这个地方是正式执行线程的代码 */try {final boolean cont = loop();codeRunLocation = 4;// 当结果返回 false 同时 falseConsumer 不为 null 时, 调用 falseConsumer 方法, 否则不做任何处理, 继续下一次循环if (!cont && falseConsumer != null) {falseConsumer.accept(getCtrlComp());}} catch (RuntimeException e) {/* 如果发生异常则调用 catchConsumer, 若 catchConsumer 为 null, 则封装现场并抛出异常 */if (catchConsumer != null) {catchConsumer.accept(getCtrlComp(), e);} else {throw new RuntimeException(String.format("CtrlLoopThread [%s] processing exception, the thread stop!", name), e);}}codeRunLocation = 5;// 线程关闭标记为true, 则关闭线程if (endFlag) {break;}// 控制loop多久循环一次, 防止 CPU 过高占用if (millisecond > 0) {try {Thread.sleep(millisecond);} catch (InterruptedException e) {log.debug("CtrlLoopThread [{}] was interrupted during sleep", name);thread.interrupt();}}}log.debug("CtrlLoopThread [{}] end!!!", name);state = -1;}}/*** 当前类的控制器*/public class CtrlComp {/*** 线程暂停指定毫秒, 同Object.wait()一样, 若等待时间为0,则表示永久暂停。* 当线程正在暂停中时, 再次调用该方法, 线程在自动结束等待情况下, 将继续 wait 指定的时间** @param waitTime 暂停的时间(毫秒)*/public void pause(long waitTime) {CtrlLoopThreadComp.this.pause(waitTime);}/*** 线程暂停*/public void pause() {CtrlLoopThreadComp.this.pause();}/*** 继续下一次循环*/public void continueNextLoop() {}/*** 终止内置线程*/public void endCtrlLoopThread() {close();}/*** 打印异常** @param e 发生异常*/public void logException(RuntimeException e) {final String msg = String.format("CtrlLoopThread [%s] processing exception, continue to the next round", getName());log.error(msg, e);}}}

git

相关源码在 github 和 gitee 上, 上面有最新的代码.

  • github: https://github.com/cosycode/common-lang
  • gitee: https://gitee.com/cosycode/common-lang

repo

同时我也将代码打包成 jar, 发布到 maven 仓库

Apache Maven

<dependency><groupId>com.github.cosycode</groupId><artifactId>common-lang</artifactId><version>1.6</version>
</dependency>

gradle

implementation 'com.github.cosycode:common-lang:1.6'

该类完整类路径是 com.github.cosycode.common.thread.CtrlLoopThreadComp


若是有什么错误或者是有什么使用建议, 欢迎大家留言

这篇关于cosycode类分享:可控制的单循环线程 com.github.cosycode.common.thread.CtrlLoopThreadComp的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1048100

相关文章

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

【专题】2024飞行汽车技术全景报告合集PDF分享(附原数据表)

原文链接: https://tecdat.cn/?p=37628 6月16日,小鹏汇天旅航者X2在北京大兴国际机场临空经济区完成首飞,这也是小鹏汇天的产品在京津冀地区进行的首次飞行。小鹏汇天方面还表示,公司准备量产,并计划今年四季度开启预售小鹏汇天分体式飞行汽车,探索分体式飞行汽车城际通勤。阅读原文,获取专题报告合集全文,解锁文末271份飞行汽车相关行业研究报告。 据悉,业内人士对飞行汽车行业

Thread如何划分为Warp?

1 .Thread如何划分为Warp? https://jielahou.com/code/cuda/thread-to-warp.html  Thread Index和Thread ID之间有什么关系呢?(线程架构参考这里:CUDA C++ Programming Guide (nvidia.com)open in new window) 1维的Thread Index,其Thread

如何提高 GitHub 的下载速度

如何提高 GitHub 的下载速度 文章目录 如何提高 GitHub 的下载速度1. 注册账号2. 准备好链接3. 创建仓库4. 在码云上下载代码5. 仓库更新了怎么办 一般来说,国内的朋友从 GitHub 上面下载代码,速度最大是 20KB/s,这种龟速,谁能忍受呢? 本文介绍一种方法——利用“码云”,可以大大提高下载速度,亲测有效。 1. 注册账号 去“码云”注册一

消除安卓SDK更新时的“https://dl-ssl.google.com refused”异常的方法

消除安卓SDK更新时的“https://dl-ssl.google.com refused”异常的方法   消除安卓SDK更新时的“https://dl-ssl.google.com refused”异常的方法 [转载]原地址:http://blog.csdn.net/x605940745/article/details/17911115 消除SDK更新时的“

java常用面试题-基础知识分享

什么是Java? Java是一种高级编程语言,旨在提供跨平台的解决方案。它是一种面向对象的语言,具有简单、结构化、可移植、可靠、安全等特点。 Java的主要特点是什么? Java的主要特点包括: 简单性:Java的语法相对简单,易于学习和使用。面向对象:Java是一种完全面向对象的语言,支持封装、继承和多态。跨平台性:Java的程序可以在不同的操作系统上运行,称为"Write once,

线程的四种操作

所属专栏:Java学习        1. 线程的开启 start和run的区别: run:描述了线程要执行的任务,也可以称为线程的入口 start:调用系统函数,真正的在系统内核中创建线程(创建PCB,加入到链表中),此处的start会根据不同的系统,分别调用不同的api,创建好之后的线程,再单独去执行run(所以说,start的本质是调用系统api,系统的api

控制反转 的种类

之前对控制反转的定义和解释都不是很清晰。最近翻书发现在《Pro Spring 5》(免费电子版在文章最后)有一段非常不错的解释。记录一下,有道翻译贴出来方便查看。如有请直接跳过中文,看后面的原文。 控制反转的类型 控制反转的类型您可能想知道为什么有两种类型的IoC,以及为什么这些类型被进一步划分为不同的实现。这个问题似乎没有明确的答案;当然,不同的类型提供了一定程度的灵活性,但

分享5款免费录屏的工具,搞定网课不怕错过!

虽然现在学生们不怎么上网课, 但是对于上班族或者是没有办法到学校参加课程的人来说,网课还是很重要的,今天,我就来跟大家分享一下我用过的几款录屏软件=,看看它们在录制网课时的表现如何。 福昕录屏大师 网址:https://www.foxitsoftware.cn/REC/ 这款软件给我的第一印象就是界面简洁,操作起来很直观。它支持全屏录制,也支持区域录制,这对于我这种需要同时录制PPT和老师讲