+ * On Java 21+, returns a builder for creating a platform {@code Thread} or {@code ThreadFactory} + * that creates virtual threads.
+ * + * @return A builder for creating {@code Thread} or {@code ThreadFactory} objects. + */ + public static Builder.OfVirtual ofVirtual() { + return getThreadProvider().ofVirtual(); + } + + /** + * A builder for {@link Thread} and {@link ThreadFactory} objects. + */ + public interface Builder { + + /** + * Sets the thread name. + * @param name thread name + * @return this builder + */ + Builder name(String name); + + /** + * Sets the thread name to be the concatenation of a string prefix and + * the string representation of a counter value. + * + * @param prefix thread name prefix + * @param start the starting value of the counter + * @return this builder + * @throws IllegalArgumentException if start is negative + */ + Builder name(String prefix, long start); + + /** + * On Java 8+, this method is a no-op.
+ * On Java 21+, sets whether the thread inherits the initial values of {@linkplain + * InheritableThreadLocal inheritable-thread-local} variables from the + * constructing thread. The default is to inherit. + * + * @param inherit {@code true} to inherit, {@code false} to not inherit + * @return this builder + */ + Builder inheritInheritableThreadLocals(boolean inherit); + + /** + * Sets the uncaught exception handler. + * @param ueh uncaught exception handler + * @return this builder + */ + Builder uncaughtExceptionHandler(UncaughtExceptionHandler ueh); + + /** + * Creates a new {@code Thread} from the current state of the builder to + * run the given task. + * + * @param task the object to run when the thread executes + * @return a new unstarted Thread + */ + Thread unstarted(Runnable task); + + /** + * Creates a new {@code Thread} from the current state of the builder and + * schedules it to execute. + * + * @param task the object to run when the thread executes + * @return a new started Thread + */ + Thread start(Runnable task); + + /** + * Returns a {@code ThreadFactory} to create threads from the current + * state of the builder. The returned thread factory is safe for use by + * multiple concurrent threads. + * + * @return a thread factory to create threads + */ + ThreadFactory factory(); + + /** + * A builder for creating a platform {@link Thread} or {@link ThreadFactory} + * that creates platform threads. + */ + interface OfPlatform extends Builder { + + @Override OfPlatform name(String name); + + /** + * @throws IllegalArgumentException {@inheritDoc} + */ + @Override OfPlatform name(String prefix, long start); + + @Override OfPlatform inheritInheritableThreadLocals(boolean inherit); + @Override OfPlatform uncaughtExceptionHandler(UncaughtExceptionHandler ueh); + + /** + * Sets the thread group. + * @param group the thread group + * @return this builder + */ + OfPlatform group(ThreadGroup group); + + /** + * Sets the daemon status. + * @param on {@code true} to create daemon threads + * @return this builder + */ + OfPlatform daemon(boolean on); + + /** + * Sets the daemon status to {@code true}. + * @return this builder + */ + default OfPlatform daemon() { + return daemon(true); + } + + /** + * Sets the thread priority. + * @param priority priority + * @return this builder + * @throws IllegalArgumentException if the priority is less than + * {@link Thread#MIN_PRIORITY} or greater than {@link Thread#MAX_PRIORITY} + */ + OfPlatform priority(int priority); + + /** + * Sets the desired stack size. + * + *
The stack size is the approximate number of bytes of address space + * that the Java virtual machine is to allocate for the thread's stack. The + * effect is highly platform dependent and the Java virtual machine is free + * to treat the {@code stackSize} parameter as a "suggestion". If the value + * is unreasonably low for the platform then a platform specific minimum + * may be used. If the value is unreasonably high then a platform specific + * maximum may be used. A value of zero is always ignored. + * + * @param stackSize the desired stack size + * @return this builder + * @throws IllegalArgumentException if the stack size is negative + */ + OfPlatform stackSize(long stackSize); + } + + /** + * A builder for creating a virtual {@link Thread} or {@link ThreadFactory} + * that creates virtual threads. + */ + interface OfVirtual extends Builder { + + @Override OfVirtual name(String name); + + /** + * @throws IllegalArgumentException {@inheritDoc} + */ + @Override OfVirtual name(String prefix, long start); + + @Override OfVirtual inheritInheritableThreadLocals(boolean inherit); + @Override OfVirtual uncaughtExceptionHandler(UncaughtExceptionHandler ueh); + } } + + private static final AtomicInteger threadNumber = new AtomicInteger(); + static String genThreadName() { + return "Thread-" + threadNumber.getAndIncrement(); + } + private ThreadTool() { throw new AssertionError(); } diff --git a/src/main/java21/com/github/thunkware/ThreadBuilders21.java b/src/main/java21/com/github/thunkware/ThreadBuilders21.java new file mode 100644 index 0000000..47ce964 --- /dev/null +++ b/src/main/java21/com/github/thunkware/ThreadBuilders21.java @@ -0,0 +1,131 @@ +package com.github.thunkware; + +import com.github.thunkware.ThreadTool.Builder.OfPlatform; +import com.github.thunkware.ThreadTool.Builder.OfVirtual; + +import java.lang.Thread.UncaughtExceptionHandler; +import java.util.concurrent.ThreadFactory; + +class ThreadBuilders21 { + private ThreadBuilders21() { + } + + static final class PlatformThreadBuilder implements OfPlatform { + private final java.lang.Thread.Builder.OfPlatform delegate = Thread.ofPlatform(); + + @Override + public Thread unstarted(Runnable task) { + return delegate.unstarted(task); + } + + @Override + public Thread start(Runnable task) { + return delegate.start(task); + } + + @Override + public ThreadFactory factory() { + return delegate.factory(); + } + + @Override + public OfPlatform name(String name) { + delegate.name(name); + return this; + } + + @Override + public OfPlatform name(String prefix, long start) { + delegate.name(prefix, start); + return this; + } + + @Override + public OfPlatform inheritInheritableThreadLocals(boolean inherit) { + delegate.inheritInheritableThreadLocals(inherit); + return this; + } + + @Override + public OfPlatform uncaughtExceptionHandler(UncaughtExceptionHandler ueh) { + delegate.uncaughtExceptionHandler(ueh); + return this; + } + + @Override + public OfPlatform group(ThreadGroup group) { + delegate.group(group); + return this; + } + + @Override + public OfPlatform daemon(boolean on) { + delegate.daemon(on); + return this; + } + + @Override + public OfPlatform daemon() { + delegate.daemon(); + return this; + } + + @Override + public OfPlatform priority(int priority) { + delegate.priority(priority); + return this; + } + + @Override + public OfPlatform stackSize(long stackSize) { + delegate.stackSize(stackSize); + return this; + } + + } + + static final class VirtualThreadBuilder implements OfVirtual { + private final java.lang.Thread.Builder.OfVirtual delegate = Thread.ofVirtual(); + + @Override + public Thread unstarted(Runnable task) { + return delegate.unstarted(task); + } + + @Override + public Thread start(Runnable task) { + return delegate.start(task); + } + + @Override + public ThreadFactory factory() { + return delegate.factory(); + } + + @Override + public OfVirtual name(String name) { + delegate.name(name); + return this; + } + + @Override + public OfVirtual name(String prefix, long start) { + delegate.name(prefix, start); + return this; + } + + @Override + public OfVirtual inheritInheritableThreadLocals(boolean inherit) { + delegate.inheritInheritableThreadLocals(inherit); + return this; + } + + @Override + public OfVirtual uncaughtExceptionHandler(UncaughtExceptionHandler ueh) { + delegate.uncaughtExceptionHandler(ueh); + return this; + } + + } + +} diff --git a/src/main/java21/com/github/thunkware/ThreadProvider21.java b/src/main/java21/com/github/thunkware/ThreadProvider21.java new file mode 100644 index 0000000..94d56ae --- /dev/null +++ b/src/main/java21/com/github/thunkware/ThreadProvider21.java @@ -0,0 +1,52 @@ +package com.github.thunkware; + +import com.github.thunkware.ThreadTool.Builder.OfPlatform; +import com.github.thunkware.ThreadTool.Builder.OfVirtual; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; + +final class ThreadProvider21 implements ThreadProvider { + + @Override + public boolean hasVirtualThreads() { + return true; + } + + @Override + public final boolean isVirtual(final Thread thread) { + return thread.isVirtual(); + } + + @Override + public Thread startVirtualThread(final Runnable task) { + return Thread.startVirtualThread(task); + } + + @Override + public Thread unstartedVirtualThread(Runnable task) { + return Thread.ofVirtual().unstarted(task); + } + + @Override + public ExecutorService newThreadPerTaskExecutor(ThreadFactory threadFactory) { + return Executors.newThreadPerTaskExecutor(threadFactory); + } + + @Override + public ExecutorService newVirtualThreadPerTaskExecutor() { + return Executors.newVirtualThreadPerTaskExecutor(); + } + + @Override + public OfPlatform ofPlatform() { + return new ThreadBuilders21.PlatformThreadBuilder(); + } + + @Override + public OfVirtual ofVirtual() { + return new ThreadBuilders21.VirtualThreadBuilder(); + } + +} diff --git a/src/test/java/com/github/thunkware/ThreadTool21Test.java b/src/test/java/com/github/thunkware/ThreadTool21Test.java index 67593aa..a0a54be 100644 --- a/src/test/java/com/github/thunkware/ThreadTool21Test.java +++ b/src/test/java/com/github/thunkware/ThreadTool21Test.java @@ -4,6 +4,7 @@ import org.junit.jupiter.api.Test; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import static org.apache.commons.lang3.JavaVersion.JAVA_20; @@ -53,4 +54,27 @@ void test() { }; assertThat(ThreadTool.unstartedVirtualThread(task).isAlive()).isFalse(); } + + @Test + void testOfPlatform() { + ThreadFactory factory = ThreadTool.ofPlatform() + .daemon(true) + .name("foo") + .factory(); + Thread thread = factory.newThread(Thread::yield); + assertThat(thread.isDaemon()).isTrue(); + assertThat(thread.getName()).isEqualTo("foo"); + assertThat(ThreadTool.isVirtual(thread)).isFalse(); + } + + @Test + void testOfVirtual() { + ThreadFactory factory = ThreadTool.ofVirtual() + .name("foo") + .factory(); + Thread thread = factory.newThread(Thread::yield); + assertThat(thread.isDaemon()).isTrue(); + assertThat(thread.getName()).isEqualTo("foo"); + assertThat(ThreadTool.isVirtual(thread)).isTrue(); + } } diff --git a/src/test/java/com/github/thunkware/ThreadTool8Test.java b/src/test/java/com/github/thunkware/ThreadTool8Test.java index a9ce32a..5c93bc7 100644 --- a/src/test/java/com/github/thunkware/ThreadTool8Test.java +++ b/src/test/java/com/github/thunkware/ThreadTool8Test.java @@ -4,6 +4,7 @@ import org.junit.jupiter.api.Test; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import static org.apache.commons.lang3.JavaVersion.JAVA_20; @@ -40,4 +41,27 @@ void test() { }; assertThat(ThreadTool.unstartedVirtualThread(task).isAlive()).isFalse(); } + + @Test + void testOfPlatform() { + ThreadFactory factory = ThreadTool.ofPlatform() + .daemon(true) + .name("foo") + .factory(); + Thread thread = factory.newThread(Thread::yield); + assertThat(thread.isDaemon()).isTrue(); + assertThat(thread.getName()).isEqualTo("foo"); + assertThat(ThreadTool.isVirtual(thread)).isFalse(); + } + + @Test + void testOfVirtual() { + ThreadFactory factory = ThreadTool.ofVirtual() + .name("foo") + .factory(); + Thread thread = factory.newThread(Thread::yield); + assertThat(thread.isDaemon()).isTrue(); + assertThat(thread.getName()).isEqualTo("foo"); + assertThat(ThreadTool.isVirtual(thread)).isFalse(); + } }