From 4a6bd38732bc6b655de6515449c34e634895d02d Mon Sep 17 00:00:00 2001 From: ehlxr Date: Fri, 4 Mar 2022 09:59:22 +0800 Subject: [PATCH] update at 2022-03-04 09:59:22 by ehlxr --- .../github/ehlxr/queue/RedisDelayQueue.java | 113 ++++++++++++++ .../ehlxr/semaphore/DistributedSemaphore.java | 44 ++++++ .../ehlxr/semaphore/RedisSemaphore.java | 147 ++++++++++++++++++ .../github/ehlxr/semaphore/SemaphoreInfo.java | 67 ++++++++ .../src/main/java/io/github/ehlxr/Main.java | 41 +++++ .../ehlxr/config/LoggerLevelRefresher.java | 65 ++++++++ .../tzld/activity/ServerApplicationTests.java | 65 ++++++++ 7 files changed, 542 insertions(+) create mode 100644 budd-common/src/main/java/io/github/ehlxr/queue/RedisDelayQueue.java create mode 100644 budd-common/src/main/java/io/github/ehlxr/semaphore/DistributedSemaphore.java create mode 100644 budd-common/src/main/java/io/github/ehlxr/semaphore/RedisSemaphore.java create mode 100644 budd-common/src/main/java/io/github/ehlxr/semaphore/SemaphoreInfo.java create mode 100644 budd-demo/src/main/java/io/github/ehlxr/Main.java create mode 100644 budd-server/src/main/java/io/github/ehlxr/config/LoggerLevelRefresher.java create mode 100644 budd-server/src/test/java/com/tzld/activity/ServerApplicationTests.java diff --git a/budd-common/src/main/java/io/github/ehlxr/queue/RedisDelayQueue.java b/budd-common/src/main/java/io/github/ehlxr/queue/RedisDelayQueue.java new file mode 100644 index 0000000..e76b85e --- /dev/null +++ b/budd-common/src/main/java/io/github/ehlxr/queue/RedisDelayQueue.java @@ -0,0 +1,113 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2022 xrv + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package io.github.ehlxr.queue; + +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * @author ehlxr + * @since 2022-02-24 20:00. + */ +@Component +public class RedisDelayQueue { + private static final Logger log = LoggerFactory.getLogger(RedisDelayQueue.class); + /** + * 延迟队列名称 + */ + private static final String DELAY_QUEUE_NAME = "budd:delayQueue"; + private static final ExecutorService POOL = new ThreadPoolExecutor(5, 200, + 0L, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue<>(1024), new ThreadFactoryBuilder() + .setNameFormat("RedisDelayQueue-pool-%d").build(), + new ThreadPoolExecutor.AbortPolicy()); + private final RedisTemplate redisTemplate; + + public RedisDelayQueue(RedisTemplate redisTemplate) { + this.redisTemplate = redisTemplate; + } + + /** + * 添加延迟任务 + * + * @param msg 延迟任务消息 + * @param delayTime 延迟时间(毫秒) + */ + public void addDelayTasks(String msg, long delayTime) { + Boolean result = redisTemplate.opsForZSet() + .add(DELAY_QUEUE_NAME, msg, System.currentTimeMillis() + delayTime); + log.debug("add task {} result {} ", msg, result); + } + + /** + * 监听延迟消息 + */ + @PostConstruct + public void listenDelayLoop() { + //noinspection AlibabaAvoidManuallyCreateThread + new Thread(() -> { + while (true) { + // 获取一个到点的消息 + Set taskSet = redisTemplate.opsForZSet() + .rangeByScore(DELAY_QUEUE_NAME, 0, System.currentTimeMillis(), 0, 1); + + // 如果没有,就等等 + if (taskSet == null || taskSet.isEmpty()) { + try { + // log.debug("there is no task will sleep 1s."); + TimeUnit.SECONDS.sleep(1); + } catch (InterruptedException e) { + log.error("listen delay loop error.", e); + } + // 继续执行 + continue; + } + + for (String task : taskSet) { + // 任务认领成功 + Long isRemove = redisTemplate.opsForZSet().remove(DELAY_QUEUE_NAME, task); + if (isRemove != null && isRemove > 0) { + log.info("will deal task {}", task); + // 拿到任务 后续处理 + POOL.execute(() -> log.info("task {} deal done.", task)); + } else { + log.warn("task {} has been handled by another instance", task); + } + } + } + }).start(); + } +} + diff --git a/budd-common/src/main/java/io/github/ehlxr/semaphore/DistributedSemaphore.java b/budd-common/src/main/java/io/github/ehlxr/semaphore/DistributedSemaphore.java new file mode 100644 index 0000000..9b237ab --- /dev/null +++ b/budd-common/src/main/java/io/github/ehlxr/semaphore/DistributedSemaphore.java @@ -0,0 +1,44 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2022 xrv + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package io.github.ehlxr.semaphore; + +/** + * @author ehlxr + * @since 2022-03-03 11:25. + */ +public interface DistributedSemaphore { + /** + * 尝试获取一个信号量 + * + * @return true 获取成功,false 获取失败 + */ + boolean tryAcquire(); + + /** + * 释放自己持有的信号量 + */ + void release(); +} + diff --git a/budd-common/src/main/java/io/github/ehlxr/semaphore/RedisSemaphore.java b/budd-common/src/main/java/io/github/ehlxr/semaphore/RedisSemaphore.java new file mode 100644 index 0000000..4e924f2 --- /dev/null +++ b/budd-common/src/main/java/io/github/ehlxr/semaphore/RedisSemaphore.java @@ -0,0 +1,147 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2022 xrv + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package io.github.ehlxr.semaphore; + +import org.springframework.dao.DataAccessException; +import org.springframework.data.redis.core.RedisOperations; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.SessionCallback; +import org.springframework.data.redis.core.ZSetOperations; + +import javax.annotation.Nonnull; +import java.util.concurrent.TimeUnit; + +/** + * @author ehlxr + * @since 2022-03-03 11:25. + */ +public class RedisSemaphore implements DistributedSemaphore { + private static final String SEMAPHORE_TIME_KEY = "budd:semaphore:time:"; + private static final String SEMAPHORE_OWNER_KEY = "budd:semaphore:owner:"; + private static final String SEMAPHORE_COUNTER_KEY = "budd:semaphore:counter:"; + private final RedisTemplate redisTemplate; + private final String timeKey; + private final String ownerKey; + private final String counterKey; + // 信号量的信息 + private final SemaphoreInfo info; + // 信号量实体 + private final DistributedSemaphore semaphore; + // 身份证明 + private final String identification; + + public RedisSemaphore(SemaphoreInfo info, RedisTemplate redisTemplate, + String identification) { + this.info = info; + this.redisTemplate = redisTemplate; + this.timeKey = SEMAPHORE_TIME_KEY.concat(info.getSemaphoreName()); + this.ownerKey = SEMAPHORE_OWNER_KEY.concat(info.getSemaphoreName()); + this.counterKey = SEMAPHORE_COUNTER_KEY.concat(info.getSemaphoreName()); + this.semaphore = info.isFair() ? new FairSemaphore() : new NonfairSemaphore(); + this.identification = identification; + } + + @Override + public boolean tryAcquire() { + return semaphore.tryAcquire(); + } + + @Override + public void release() { + semaphore.release(); + } + + private class NonfairSemaphore implements DistributedSemaphore { + @Override + public boolean tryAcquire() { + ZSetOperations zsetOps = redisTemplate.opsForZSet(); + long timeMillis = System.currentTimeMillis(); + // 先清除过期的信号量 + zsetOps.removeRangeByScore(timeKey, 0, timeMillis - TimeUnit.SECONDS.toMillis(info.getExpire())); + // 尝试获取信号量并比较自身的排名,如果小于许可证的数量则表示获取成功 (redis rank 指令从 0 开始计数) + zsetOps.add(timeKey, identification, timeMillis); + Long rank = zsetOps.rank(timeKey, identification); + if (rank != null && rank < info.getPermits()) { + return true; + } + // 获取失败,删除掉上边添加的标识 + release(); + return false; + } + + @Override + public void release() { + redisTemplate.opsForZSet().remove(timeKey, identification); + } + } + + private class FairSemaphore implements DistributedSemaphore { + @Override + public boolean tryAcquire() { + long timeMillis = System.currentTimeMillis(); + // 用于获取信号量的计数 + Long counter = redisTemplate.opsForValue().increment(counterKey, 1); + if (counter == null) { + return false; + } + // 用流水线把这一堆命令用一次 IO 全部发过去 + redisTemplate.executePipelined(new SessionCallback<>() { + @Override + public Object execute(@Nonnull RedisOperations operations) throws DataAccessException { + ZSetOperations zsetOps = operations.opsForZSet(); + // 利用 timeKey 来控制是否过期,ownerKey 控制是否超配额, + // 使用 zset 的 intersectAndStore 在获取信号量之前清理过期数据,同时清理排名集合中的数据 + // 清除过期的信号量 + zsetOps.removeRangeByScore(timeKey, 0, timeMillis - TimeUnit.SECONDS.toMillis(info.getExpire())); + zsetOps.intersectAndStore(timeKey, ownerKey, ownerKey); + // 尝试获取信号量 + zsetOps.add(timeKey, identification, timeMillis); + zsetOps.add(ownerKey, identification, counter); + return null; + } + }); + // 这里根据 持有者集合 的分数来进行判断 + Long ownerRank = redisTemplate.opsForZSet().rank(ownerKey, identification); + if (ownerRank != null && ownerRank < info.getPermits()) { + return true; + } + release(); + return false; + } + + @Override + public void release() { + redisTemplate.executePipelined(new SessionCallback<>() { + @Override + public Object execute(@Nonnull RedisOperations operations) throws DataAccessException { + ZSetOperations zetOps = operations.opsForZSet(); + zetOps.remove(timeKey, identification); + zetOps.remove(ownerKey, identification); + return null; + } + }); + } + } +} \ No newline at end of file diff --git a/budd-common/src/main/java/io/github/ehlxr/semaphore/SemaphoreInfo.java b/budd-common/src/main/java/io/github/ehlxr/semaphore/SemaphoreInfo.java new file mode 100644 index 0000000..473d635 --- /dev/null +++ b/budd-common/src/main/java/io/github/ehlxr/semaphore/SemaphoreInfo.java @@ -0,0 +1,67 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2022 xrv + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package io.github.ehlxr.semaphore; + +/** + * @author ehlxr + * @since 2022-03-03 11:24. + */ +public final class SemaphoreInfo { + // 信号量的名称 + private final String semaphoreName; + // 许可证的数量 + private final int permits; + // 信号量最大持有时间 (过期时间) 单位 s + private final long expire; + // 公平 or 非公平 + private final boolean fair; + + public SemaphoreInfo(String semaphoreName, int permits, long expire) { + this(semaphoreName, permits, expire, false); + } + + public SemaphoreInfo(String semaphoreName, int permits, long expire, boolean fair) { + this.semaphoreName = semaphoreName; + this.permits = permits; + this.expire = expire; + this.fair = fair; + } + + public String getSemaphoreName() { + return semaphoreName; + } + + public int getPermits() { + return permits; + } + + public long getExpire() { + return expire; + } + + public boolean isFair() { + return fair; + } +} \ No newline at end of file diff --git a/budd-demo/src/main/java/io/github/ehlxr/Main.java b/budd-demo/src/main/java/io/github/ehlxr/Main.java new file mode 100644 index 0000000..5bb1b37 --- /dev/null +++ b/budd-demo/src/main/java/io/github/ehlxr/Main.java @@ -0,0 +1,41 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2022 xrv + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package io.github.ehlxr; + +/** + * @author ehlxr + * @since 2022-01-17 06:31. + */ +public class Main { + public static void main(String[] args) { + int[][] ns = { + { 1, 2, 3, 4 }, + { 5, 6, 7, 8 }, + { 9, 10, 11, 12 } + }; + System.out.println(ns.length); // 3 + System.out.println(ns[0][2]); + } +} diff --git a/budd-server/src/main/java/io/github/ehlxr/config/LoggerLevelRefresher.java b/budd-server/src/main/java/io/github/ehlxr/config/LoggerLevelRefresher.java new file mode 100644 index 0000000..625c88c --- /dev/null +++ b/budd-server/src/main/java/io/github/ehlxr/config/LoggerLevelRefresher.java @@ -0,0 +1,65 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2022 xrv + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package io.github.ehlxr.config; + +// import com.ctrip.framework.apollo.model.ConfigChangeEvent; +// import com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.logging.LoggingSystem; +// import org.springframework.stereotype.Component; + +/** + * 结合 apollo 配置中心动态刷新(logging.level.)日志级别 + * + * @author ehlxr + * @since 2022-01-06 17:43. + */ +// @Component +public class LoggerLevelRefresher { + private static final Logger logger = LoggerFactory.getLogger(LoggerLevelRefresher.class); + private static final String LOGGER_TAG = "logging.level."; + private final LoggingSystem loggingSystem; + + @Autowired + public LoggerLevelRefresher(LoggingSystem loggingSystem) { + this.loggingSystem = loggingSystem; + } + + // @ApolloConfigChangeListener(interestedKeyPrefixes = LOGGER_TAG) + // private void onChange(ConfigChangeEvent changeEvent) { + // for (String key : changeEvent.changedKeys()) { + // String strLevel = changeEvent.getChange(key).getNewValue(); + // loggingSystem.setLogLevel(key.replaceAll(LOGGER_TAG, ""), + // LogLevel.valueOf(strLevel.toUpperCase())); + // + // logger.info("logging changed: {}, oldValue: {}, newValue: {}", + // key, changeEvent.getChange(key).getOldValue(), strLevel); + // } + // } + +} \ No newline at end of file diff --git a/budd-server/src/test/java/com/tzld/activity/ServerApplicationTests.java b/budd-server/src/test/java/com/tzld/activity/ServerApplicationTests.java new file mode 100644 index 0000000..f1d5cbd --- /dev/null +++ b/budd-server/src/test/java/com/tzld/activity/ServerApplicationTests.java @@ -0,0 +1,65 @@ +package com.tzld.activity; + +import io.github.ehlxr.semaphore.RedisSemaphore; +import io.github.ehlxr.semaphore.SemaphoreInfo; +import org.junit.jupiter.api.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.test.context.junit4.SpringRunner; + +import java.util.concurrent.*; + +@RunWith(SpringRunner.class) +@SpringBootTest() +class ServerApplicationTests { + + ThreadPoolExecutor pool = new ThreadPoolExecutor(10, 10, 0, TimeUnit.MINUTES, + new LinkedBlockingQueue<>(), + Executors.defaultThreadFactory(), + new ThreadPoolExecutor.CallerRunsPolicy()); + @Autowired + private RedisTemplate redisTemplate; + + @Test + public void testNonFair() throws InterruptedException { + SemaphoreInfo semaphoreInfo = new SemaphoreInfo("NonFair", 5, 10); + for (int i = 0; i < 10; i++) { + String id = String.valueOf(i); + RedisSemaphore semaphore = new RedisSemaphore(semaphoreInfo, redisTemplate, id); + CompletableFuture.supplyAsync(semaphore::tryAcquire, pool).thenAcceptAsync((r) -> { + if (r) System.out.println(id + "成功获取到信号量 (NonFair)~ ⭐⭐⭐"); + else System.out.println(id + "没有获取到信号量 (NonFair)"); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + semaphore.release(); + }, pool); + } + Thread.sleep(Long.MAX_VALUE); + } + + @Test + public void testFair() throws ExecutionException, InterruptedException { + SemaphoreInfo semaphoreInfo = new SemaphoreInfo("Fair", 5, 10, true); + for (int i = 0; i < 10; i++) { + String id = String.valueOf(i); + RedisSemaphore semaphore = new RedisSemaphore(semaphoreInfo, redisTemplate, id); + CompletableFuture.supplyAsync(semaphore::tryAcquire, pool).thenAcceptAsync((r) -> { + if (r) System.out.println(id + "成功获取到信号量 (Fair)~~ ⭐⭐⭐"); + else System.out.println(id + "没有获取到信号量 (Fair)"); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + semaphore.release(); + }, pool); + } + Thread.sleep(Long.MAX_VALUE); + } + +}