From f9f60e1489ecad64ba657dbe5f8d38e8f004800f Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sun, 19 Jul 2020 18:23:47 -0400 Subject: [PATCH] Small code cleanups and switch more stuff to adventure --- .../com/velocitypowered/proxy/Metrics.java | 2 +- .../connection/client/ConnectedPlayer.java | 6 ++-- .../client/LoginSessionHandler.java | 9 ++--- .../proxy/network/ConnectionManager.java | 2 -- .../netty/SeparatePoolInetNameResolver.java | 33 ++++++++++++++++++- .../proxy/protocol/ProtocolUtils.java | 8 ++--- .../protocol/netty/AutoReadHolderHandler.java | 2 +- .../proxy/protocol/util/ByteBufDataInput.java | 2 +- .../protocol/util/ByteBufDataOutput.java | 2 +- .../protocol/util/FaviconSerializer.java | 2 +- .../protocol/util/GameProfileSerializer.java | 2 +- .../protocol/util/NettyPreconditions.java | 2 +- .../protocol/util/PluginMessageUtil.java | 12 +++---- .../proxy/util/AddressUtil.java | 6 ++-- .../proxy/util/DurationUtils.java | 2 +- .../proxy/util/VelocityMessages.java | 2 +- .../proxy/util/bossbar/BossBarManager.java | 2 +- .../proxy/util/concurrent/Once.java | 19 ++++------- .../proxy/util/concurrent/OnceTest.java | 8 +++-- 19 files changed, 75 insertions(+), 48 deletions(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/Metrics.java b/proxy/src/main/java/com/velocitypowered/proxy/Metrics.java index 2dd7f48e4..b436740da 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/Metrics.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/Metrics.java @@ -43,7 +43,7 @@ public class Metrics { private static boolean logFailedRequests = false; // The logger for the failed requests - private static Logger logger = LogManager.getLogger(Metrics.class); + private static final Logger logger = LogManager.getLogger(Metrics.class); // The name of the server software private final String name; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java index b3aa72c46..b9dd37992 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java @@ -568,7 +568,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { if (event.getResult() instanceof DisconnectPlayer) { DisconnectPlayer res = (DisconnectPlayer) event.getResult(); - disconnect(res.getReason()); + disconnect(res.getReasonComponent()); } else if (event.getResult() instanceof RedirectPlayer) { RedirectPlayer res = (RedirectPlayer) event.getResult(); createConnectionRequest(res.getServer()) @@ -587,9 +587,9 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { } else if (event.getResult() instanceof Notify) { Notify res = (Notify) event.getResult(); if (event.kickedDuringServerConnect()) { - sendMessage(res.getMessage()); + sendMessage(res.getMessageComponent()); } else { - disconnect(res.getMessage()); + disconnect(res.getMessageComponent()); } } else { // In case someone gets creative, assume we want to disconnect the player. diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginSessionHandler.java index 991954731..b49d46a7c 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginSessionHandler.java @@ -43,7 +43,7 @@ import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.ThreadLocalRandom; -import net.kyori.text.Component; +import net.kyori.adventure.text.Component; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.asynchttpclient.ListenableFuture; @@ -167,10 +167,11 @@ public class LoginSessionHandler implements MinecraftSessionHandler { } PreLoginComponentResult result = event.getResult(); - Optional disconnectReason = result.getReason(); + Optional disconnectReason = result.getReasonComponent(); if (disconnectReason.isPresent()) { // The component is guaranteed to be provided if the connection was denied. - mcConnection.closeWith(Disconnect.create(disconnectReason.get())); + mcConnection.closeWith(Disconnect.create(disconnectReason.get(), + inbound.getProtocolVersion())); return; } @@ -260,7 +261,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler { return; } - Optional reason = event.getResult().getReason(); + Optional reason = event.getResult().getReasonComponent(); if (reason.isPresent()) { player.disconnect0(reason.get(), true); } else { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java b/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java index 24fe82440..aacde7c3a 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java @@ -16,8 +16,6 @@ import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.WriteBufferWaterMark; import io.netty.channel.epoll.EpollChannelOption; -import io.netty.resolver.dns.DnsAddressResolverGroup; -import io.netty.resolver.dns.DnsNameResolverBuilder; import io.netty.util.concurrent.GlobalEventExecutor; import java.net.InetSocketAddress; import java.util.HashMap; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/network/netty/SeparatePoolInetNameResolver.java b/proxy/src/main/java/com/velocitypowered/proxy/network/netty/SeparatePoolInetNameResolver.java index 8fef724af..81dc6340d 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/network/netty/SeparatePoolInetNameResolver.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/network/netty/SeparatePoolInetNameResolver.java @@ -1,5 +1,7 @@ package com.velocitypowered.proxy.network.netty; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; import com.google.common.util.concurrent.ThreadFactoryBuilder; import io.netty.resolver.AddressResolver; import io.netty.resolver.AddressResolverGroup; @@ -14,11 +16,13 @@ import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.TimeUnit; public final class SeparatePoolInetNameResolver extends InetNameResolver { private final ExecutorService resolveExecutor; private final InetNameResolver delegate; + private final Cache> cache; private AddressResolverGroup resolverGroup; /** @@ -35,12 +39,28 @@ public final class SeparatePoolInetNameResolver extends InetNameResolver { .setDaemon(true) .build()); this.delegate = new DefaultNameResolver(executor); + this.cache = CacheBuilder.newBuilder() + .expireAfterWrite(30, TimeUnit.SECONDS) + .build(); } @Override protected void doResolve(String inetHost, Promise promise) throws Exception { + List addresses = cache.getIfPresent(inetHost); + if (addresses != null) { + promise.trySuccess(addresses.get(0)); + return; + } + try { - resolveExecutor.execute(() -> this.delegate.resolve(inetHost, promise)); + resolveExecutor.execute(() -> { + promise.addListener(future -> { + if (future.isSuccess()) { + cache.put(inetHost, (List) future.getNow()); + } + }); + this.delegate.resolve(inetHost, promise); + }); } catch (RejectedExecutionException e) { promise.setFailure(e); } @@ -49,7 +69,18 @@ public final class SeparatePoolInetNameResolver extends InetNameResolver { @Override protected void doResolveAll(String inetHost, Promise> promise) throws Exception { + List addresses = cache.getIfPresent(inetHost); + if (addresses != null) { + promise.trySuccess(addresses); + return; + } + try { + promise.addListener(future -> { + if (future.isSuccess()) { + cache.put(inetHost, (List) future.getNow()); + } + }); resolveExecutor.execute(() -> this.delegate.resolveAll(inetHost, promise)); } catch (RejectedExecutionException e) { promise.setFailure(e); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java index 1c1fc05f2..8ad0e1980 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java @@ -340,7 +340,7 @@ public enum ProtocolUtils { // No vanilla packet should give a 3 byte packet int len = readExtendedForgeShort(buf); - Preconditions.checkArgument(len <= (FORGE_MAX_ARRAY_LENGTH), + checkArgument(len <= FORGE_MAX_ARRAY_LENGTH, "Cannot receive array longer than %s (got %s bytes)", FORGE_MAX_ARRAY_LENGTH, len); byte[] ret = new byte[len]; @@ -360,7 +360,7 @@ public enum ProtocolUtils { // No vanilla packet should give a 3 byte packet int len = readExtendedForgeShort(buf); - checkFrame(len <= (FORGE_MAX_ARRAY_LENGTH), + checkFrame(len <= FORGE_MAX_ARRAY_LENGTH, "Cannot receive array longer than %s (got %s bytes)", FORGE_MAX_ARRAY_LENGTH, len); return buf.readRetainedSlice(len); @@ -375,7 +375,7 @@ public enum ProtocolUtils { */ public static void writeByteArray17(byte[] b, ByteBuf buf, boolean allowExtended) { if (allowExtended) { - checkFrame(b.length <= (FORGE_MAX_ARRAY_LENGTH), + checkFrame(b.length <= FORGE_MAX_ARRAY_LENGTH, "Cannot send array longer than %s (got %s bytes)", FORGE_MAX_ARRAY_LENGTH, b.length); } else { @@ -399,7 +399,7 @@ public enum ProtocolUtils { */ public static void writeByteBuf17(ByteBuf b, ByteBuf buf, boolean allowExtended) { if (allowExtended) { - checkFrame(b.readableBytes() <= (FORGE_MAX_ARRAY_LENGTH), + checkFrame(b.readableBytes() <= FORGE_MAX_ARRAY_LENGTH, "Cannot send array longer than %s (got %s bytes)", FORGE_MAX_ARRAY_LENGTH, b.readableBytes()); } else { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/AutoReadHolderHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/AutoReadHolderHandler.java index 68f1d65d5..15700673e 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/AutoReadHolderHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/AutoReadHolderHandler.java @@ -11,7 +11,7 @@ import java.util.Queue; * A variation on {@link io.netty.handler.flow.FlowControlHandler} that explicitly holds messages * on {@code channelRead} and only releases them on an explicit read operation. */ -public class AutoReadHolderHandler extends ChannelDuplexHandler implements ChannelInboundHandler { +public class AutoReadHolderHandler extends ChannelDuplexHandler { private final Queue queuedMessages; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ByteBufDataInput.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ByteBufDataInput.java index 43b6d44aa..84866012a 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ByteBufDataInput.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ByteBufDataInput.java @@ -10,7 +10,7 @@ import java.io.IOException; * A wrapper around {@link io.netty.buffer.ByteBuf} that implements the exception-free * {@link ByteArrayDataInput} interface from Guava. */ -public class ByteBufDataInput implements ByteArrayDataInput, DataInput { +public class ByteBufDataInput implements ByteArrayDataInput { private final ByteBuf in; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ByteBufDataOutput.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ByteBufDataOutput.java index b894525ab..cfc101f5f 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ByteBufDataOutput.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ByteBufDataOutput.java @@ -12,7 +12,7 @@ import java.nio.charset.StandardCharsets; /** * A {@link DataOutput} equivalent to {@link ByteBufDataInput}. */ -public class ByteBufDataOutput extends OutputStream implements DataOutput, ByteArrayDataOutput { +public class ByteBufDataOutput extends OutputStream implements ByteArrayDataOutput { private final ByteBuf buf; private final DataOutputStream utf8out; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/FaviconSerializer.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/FaviconSerializer.java index 6e4cb72fa..3d6ea395f 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/FaviconSerializer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/FaviconSerializer.java @@ -9,7 +9,7 @@ import com.google.gson.JsonSerializer; import com.velocitypowered.api.util.Favicon; import java.lang.reflect.Type; -public class FaviconSerializer implements JsonSerializer, JsonDeserializer { +public final class FaviconSerializer implements JsonSerializer, JsonDeserializer { @Override public Favicon deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/GameProfileSerializer.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/GameProfileSerializer.java index 600312b90..b8a53b29d 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/GameProfileSerializer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/GameProfileSerializer.java @@ -13,7 +13,7 @@ import com.velocitypowered.api.util.GameProfile.Property; import java.lang.reflect.Type; import java.util.List; -public class GameProfileSerializer implements JsonSerializer, +public final class GameProfileSerializer implements JsonSerializer, JsonDeserializer { private static final Type propertyList = new TypeToken>() {}.getType(); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/NettyPreconditions.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/NettyPreconditions.java index ebc76e2ad..cd59316e4 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/NettyPreconditions.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/NettyPreconditions.java @@ -8,7 +8,7 @@ import io.netty.handler.codec.CorruptedFrameException; /** * Extends {@link com.google.common.base.Preconditions} for Netty's {@link CorruptedFrameException}. */ -public class NettyPreconditions { +public final class NettyPreconditions { private static final QuietDecoderException BAD = new QuietDecoderException( "Invalid packet received. Launch Velocity with -Dvelocity.packet-decode-logging=true " + "to see more."); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/PluginMessageUtil.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/PluginMessageUtil.java index fd9d3601e..db9964708 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/PluginMessageUtil.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/PluginMessageUtil.java @@ -17,7 +17,7 @@ import java.util.List; import java.util.Locale; import java.util.regex.Pattern; -public class PluginMessageUtil { +public final class PluginMessageUtil { private static final String BRAND_CHANNEL_LEGACY = "MC|Brand"; private static final String BRAND_CHANNEL = "minecraft:brand"; @@ -110,8 +110,8 @@ public class PluginMessageUtil { */ public static PluginMessage constructChannelsPacket(ProtocolVersion protocolVersion, Collection channels) { - Preconditions.checkNotNull(channels, "channels"); - Preconditions.checkArgument(channels.size() > 0, "no channels specified"); + checkNotNull(channels, "channels"); + checkArgument(!channels.isEmpty(), "no channels specified"); String channelName = protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_13) >= 0 ? REGISTER_CHANNEL : REGISTER_CHANNEL_LEGACY; ByteBuf contents = Unpooled.buffer(); @@ -131,14 +131,14 @@ public class PluginMessageUtil { checkNotNull(version, "version"); checkArgument(isMcBrand(message), "message is not a brand plugin message"); - String toAppend = " (" + version.getName() + ")"; String currentBrand = readBrandMessage(message.content()); + String rewrittenBrand = String.format("%s (%s)", currentBrand, version.getName()); ByteBuf rewrittenBuf = Unpooled.buffer(); if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) { - ProtocolUtils.writeString(rewrittenBuf, currentBrand + toAppend); + ProtocolUtils.writeString(rewrittenBuf, rewrittenBrand); } else { - rewrittenBuf.writeBytes((currentBrand + toAppend).getBytes()); + rewrittenBuf.writeCharSequence(rewrittenBrand, StandardCharsets.UTF_8); } return new PluginMessage(message.getChannel(), rewrittenBuf); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/util/AddressUtil.java b/proxy/src/main/java/com/velocitypowered/proxy/util/AddressUtil.java index 033babcb9..b4feb268b 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/util/AddressUtil.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/util/AddressUtil.java @@ -6,13 +6,13 @@ import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.URI; -public class AddressUtil { +public final class AddressUtil { private AddressUtil() { throw new AssertionError(); } /** - * Attempts to parse an IP address of the form 127.0.0.1:25565. The returned + * Attempts to parse an IP address of the form {@code 127.0.0.1:25565}. The returned * {@link InetSocketAddress} is not resolved. * * @param ip the IP to parse @@ -30,7 +30,7 @@ public class AddressUtil { } /** - * Attempts to parse an IP address of the form 127.0.0.1:25565. The returned + * Attempts to parse an IP address of the form {@code 127.0.0.1:25565}. The returned * {@link InetSocketAddress} is resolved. * * @param ip the IP to parse diff --git a/proxy/src/main/java/com/velocitypowered/proxy/util/DurationUtils.java b/proxy/src/main/java/com/velocitypowered/proxy/util/DurationUtils.java index d2bb07f23..4742613e9 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/util/DurationUtils.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/util/DurationUtils.java @@ -5,7 +5,7 @@ import java.time.Duration; /** * Provides utility functions for working with durations. */ -public class DurationUtils { +public final class DurationUtils { private static final long ONE_TICK_IN_MILLISECONDS = 50; private DurationUtils() { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/util/VelocityMessages.java b/proxy/src/main/java/com/velocitypowered/proxy/util/VelocityMessages.java index d8ea66789..6990396f8 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/util/VelocityMessages.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/util/VelocityMessages.java @@ -4,7 +4,7 @@ import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.format.NamedTextColor; -public class VelocityMessages { +public final class VelocityMessages { public static final Component ONLINE_MODE_ONLY = TextComponent .builder("This server only accepts connections from online-mode clients.") diff --git a/proxy/src/main/java/com/velocitypowered/proxy/util/bossbar/BossBarManager.java b/proxy/src/main/java/com/velocitypowered/proxy/util/bossbar/BossBarManager.java index 1ad600812..223c5ed25 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/util/bossbar/BossBarManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/util/bossbar/BossBarManager.java @@ -57,7 +57,7 @@ public class BossBarManager implements BossBar.Listener { } private BossBarHolder getOrCreateHandler(BossBar bar) { - BossBarHolder holder = this.bars.computeIfAbsent(bar, (k) -> new BossBarHolder(bar)); + BossBarHolder holder = this.bars.computeIfAbsent(bar, k -> new BossBarHolder(bar)); holder.register(); return holder; } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/util/concurrent/Once.java b/proxy/src/main/java/com/velocitypowered/proxy/util/concurrent/Once.java index aa02f4b56..f921d26dc 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/util/concurrent/Once.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/util/concurrent/Once.java @@ -1,17 +1,12 @@ package com.velocitypowered.proxy.util.concurrent; -import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; - /** - * A class that guarantees that a given initialization shall only happen once. The implementation + * A class that guarantees that a given initialization shall only occur once. The implementation * is (almost) a direct Java port of the Go {@code sync.Once} type (see the * Go documentation) and thus has similar * semantics. */ public final class Once { - private static final AtomicIntegerFieldUpdater COMPLETED_UPDATER = - AtomicIntegerFieldUpdater.newUpdater(Once.class, "completed"); - private static final int NOT_STARTED = 0; private static final int COMPLETED = 1; @@ -20,25 +15,25 @@ public final class Once { /** * Calls {@code runnable.run()} exactly once if this instance is being called for the first time, - * otherwise the invocation shall wait until {@code runnable.run()} completes. Future calls to - * this method once {@code runnable.run()} completes are no-ops - a new instance should be used - * instead. + * otherwise the invocation shall wait until {@code runnable.run()} completes. The first runnable + * used when this function is called is run. Future calls to this method once the initial + * runnable completes are no-ops - a new instance should be used instead. * * @param runnable the runnable to run */ public void run(Runnable runnable) { - if (COMPLETED_UPDATER.get(this) == NOT_STARTED) { + if (completed == NOT_STARTED) { slowRun(runnable); } } private void slowRun(Runnable runnable) { synchronized (lock) { - if (this.completed == NOT_STARTED) { + if (completed == NOT_STARTED) { try { runnable.run(); } finally { - COMPLETED_UPDATER.set(this, COMPLETED); + completed = COMPLETED; } } } diff --git a/proxy/src/test/java/com/velocitypowered/proxy/util/concurrent/OnceTest.java b/proxy/src/test/java/com/velocitypowered/proxy/util/concurrent/OnceTest.java index 4cfb07c70..f84b81159 100644 --- a/proxy/src/test/java/com/velocitypowered/proxy/util/concurrent/OnceTest.java +++ b/proxy/src/test/java/com/velocitypowered/proxy/util/concurrent/OnceTest.java @@ -21,11 +21,13 @@ public class OnceTest { @Test void contendedOnce() throws Exception { - ExecutorService service = Executors.newFixedThreadPool(10); + int threadsForTest = 25; + + ExecutorService service = Executors.newFixedThreadPool(threadsForTest); AtomicInteger i = new AtomicInteger(); Once once = new Once(); - CountDownLatch latch = new CountDownLatch(10); - for (int i1 = 0; i1 < 10; i1++) { + CountDownLatch latch = new CountDownLatch(threadsForTest); + for (int i1 = 0; i1 < threadsForTest; i1++) { service.execute(() -> { once.run(i::incrementAndGet); latch.countDown();