jdk-24/test/jdk/java/lang/Thread/BuilderTest.java
Alan Bateman 2586f36120 8304919: Implementation of Virtual Threads
Reviewed-by: lmesnik, cjplummer, psandoz, mchung, sspitsyn, jpai
2023-04-11 05:49:54 +00:00

806 lines
28 KiB
Java

/*
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @summary Unit test for Thread.Builder
* @run junit BuilderTest
*/
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.LockSupport;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assumptions.*;
class BuilderTest {
/**
* Test Thread.ofPlatform to create platform threads.
*/
@Test
void testPlatformThread() throws Exception {
Thread parent = Thread.currentThread();
Thread.Builder.OfPlatform builder = Thread.ofPlatform();
// unstarted
AtomicBoolean done1 = new AtomicBoolean();
Thread thread1 = builder.unstarted(() -> done1.set(true));
assertFalse(thread1.isVirtual());
assertTrue(thread1.getState() == Thread.State.NEW);
assertFalse(thread1.getName().isEmpty());
assertTrue(thread1.getThreadGroup() == parent.getThreadGroup());
assertTrue(thread1.isDaemon() == parent.isDaemon());
assertTrue(thread1.getPriority() == parent.getPriority());
assertTrue(thread1.getContextClassLoader() == parent.getContextClassLoader());
thread1.start();
thread1.join();
assertTrue(done1.get());
// start
AtomicBoolean done2 = new AtomicBoolean();
Thread thread2 = builder.start(() -> done2.set(true));
assertFalse(thread2.isVirtual());
assertTrue(thread2.getState() != Thread.State.NEW);
assertFalse(thread2.getName().isEmpty());
ThreadGroup group2 = thread2.getThreadGroup();
assertTrue(group2 == parent.getThreadGroup() || group2 == null);
assertTrue(thread2.isDaemon() == parent.isDaemon());
assertTrue(thread2.getPriority() == parent.getPriority());
assertTrue(thread2.getContextClassLoader() == parent.getContextClassLoader());
thread2.join();
assertTrue(done2.get());
// factory
AtomicBoolean done3 = new AtomicBoolean();
Thread thread3 = builder.factory().newThread(() -> done3.set(true));
assertFalse(thread3.isVirtual());
assertTrue(thread3.getState() == Thread.State.NEW);
assertFalse(thread3.getName().isEmpty());
assertTrue(thread3.getThreadGroup() == parent.getThreadGroup());
assertTrue(thread3.isDaemon() == parent.isDaemon());
assertTrue(thread3.getPriority() == parent.getPriority());
assertTrue(thread3.getContextClassLoader() == parent.getContextClassLoader());
thread3.start();
thread3.join();
assertTrue(done3.get());
}
/**
* Test Thread.ofVirtual to create virtual threads.
*/
@Test
void testVirtualThread() throws Exception {
Thread parent = Thread.currentThread();
Thread.Builder.OfVirtual builder = Thread.ofVirtual();
// unstarted
AtomicBoolean done1 = new AtomicBoolean();
Thread thread1 = builder.unstarted(() -> done1.set(true));
assertTrue(thread1.isVirtual());
assertTrue(thread1.getState() == Thread.State.NEW);
assertTrue(thread1.getName().isEmpty());
assertTrue(thread1.getContextClassLoader() == parent.getContextClassLoader());
assertTrue(thread1.isDaemon());
assertTrue(thread1.getPriority() == Thread.NORM_PRIORITY);
thread1.start();
thread1.join();
assertTrue(done1.get());
// start
AtomicBoolean done2 = new AtomicBoolean();
Thread thread2 = builder.start(() -> done2.set(true));
assertTrue(thread2.isVirtual());
assertTrue(thread2.getState() != Thread.State.NEW);
assertTrue(thread2.getName().isEmpty());
assertTrue(thread2.getContextClassLoader() == parent.getContextClassLoader());
assertTrue(thread2.isDaemon());
assertTrue(thread2.getPriority() == Thread.NORM_PRIORITY);
thread2.join();
assertTrue(done2.get());
// factory
AtomicBoolean done3 = new AtomicBoolean();
Thread thread3 = builder.factory().newThread(() -> done3.set(true));
assertTrue(thread3.isVirtual());
assertTrue(thread3.getState() == Thread.State.NEW);
assertTrue(thread3.getName().isEmpty());
assertTrue(thread3.getContextClassLoader() == parent.getContextClassLoader());
assertTrue(thread3.isDaemon());
assertTrue(thread3.getPriority() == Thread.NORM_PRIORITY);
thread3.start();
thread3.join();
assertTrue(done3.get());
}
/**
* Test Thread.Builder.name.
*/
@Test
void testName1() {
Thread.Builder builder = Thread.ofPlatform().name("duke");
Thread thread1 = builder.unstarted(() -> { });
Thread thread2 = builder.start(() -> { });
Thread thread3 = builder.factory().newThread(() -> { });
assertTrue(thread1.getName().equals("duke"));
assertTrue(thread2.getName().equals("duke"));
assertTrue(thread3.getName().equals("duke"));
}
@Test
void testName2() {
Thread.Builder builder = Thread.ofVirtual().name("duke");
Thread thread1 = builder.unstarted(() -> { });
Thread thread2 = builder.start(() -> { });
Thread thread3 = builder.factory().newThread(() -> { });
assertTrue(thread1.getName().equals("duke"));
assertTrue(thread2.getName().equals("duke"));
assertTrue(thread3.getName().equals("duke"));
}
@Test
void testName3() {
Thread.Builder builder = Thread.ofPlatform().name("duke-", 100);
Thread thread1 = builder.unstarted(() -> { });
Thread thread2 = builder.unstarted(() -> { });
Thread thread3 = builder.unstarted(() -> { });
assertTrue(thread1.getName().equals("duke-100"));
assertTrue(thread2.getName().equals("duke-101"));
assertTrue(thread3.getName().equals("duke-102"));
ThreadFactory factory = builder.factory();
Thread thread4 = factory.newThread(() -> { });
Thread thread5 = factory.newThread(() -> { });
Thread thread6 = factory.newThread(() -> { });
assertTrue(thread4.getName().equals("duke-103"));
assertTrue(thread5.getName().equals("duke-104"));
assertTrue(thread6.getName().equals("duke-105"));
}
@Test
void testName4() {
Thread.Builder builder = Thread.ofVirtual().name("duke-", 100);
Thread thread1 = builder.unstarted(() -> { });
Thread thread2 = builder.unstarted(() -> { });
Thread thread3 = builder.unstarted(() -> { });
assertTrue(thread1.getName().equals("duke-100"));
assertTrue(thread2.getName().equals("duke-101"));
assertTrue(thread3.getName().equals("duke-102"));
ThreadFactory factory = builder.factory();
Thread thread4 = factory.newThread(() -> { });
Thread thread5 = factory.newThread(() -> { });
Thread thread6 = factory.newThread(() -> { });
assertTrue(thread4.getName().equals("duke-103"));
assertTrue(thread5.getName().equals("duke-104"));
assertTrue(thread6.getName().equals("duke-105"));
}
/**
* Test Thread.Builder.OfPlatform.group.
*/
@Test
void testThreadGroup1() {
ThreadGroup group = new ThreadGroup("groupies");
Thread.Builder builder = Thread.ofPlatform().group(group);
Thread thread1 = builder.unstarted(() -> { });
AtomicBoolean done = new AtomicBoolean();
Thread thread2 = builder.start(() -> {
while (!done.get()) {
LockSupport.park();
}
});
Thread thread3 = builder.factory().newThread(() -> { });
try {
assertTrue(thread1.getThreadGroup() == group);
assertTrue(thread2.getThreadGroup() == group);
assertTrue(thread3.getThreadGroup() == group);
} finally {
done.set(true);
LockSupport.unpark(thread2);
}
}
@Test
void testThreadGroup2() {
ThreadGroup vgroup = Thread.ofVirtual().unstarted(() -> { }).getThreadGroup();
assertEquals("VirtualThreads", vgroup.getName());
Thread thread1 = Thread.ofVirtual().unstarted(() -> { });
Thread thread2 = Thread.ofVirtual().start(LockSupport::park);
Thread thread3 = Thread.ofVirtual().factory().newThread(() -> { });
try {
assertTrue(thread1.getThreadGroup() == vgroup);
assertTrue(thread2.getThreadGroup() == vgroup);
assertTrue(thread3.getThreadGroup() == vgroup);
} finally {
LockSupport.unpark(thread2);
}
}
/**
* Test Thread.Builder.OfPlatform.priority.
*/
@Test
void testPriority1() {
int priority = Thread.currentThread().getPriority();
Thread.Builder builder = Thread.ofPlatform();
Thread thread1 = builder.unstarted(() -> { });
Thread thread2 = builder.start(() -> { });
Thread thread3 = builder.factory().newThread(() -> { });
assertTrue(thread1.getPriority() == priority);
assertTrue(thread2.getPriority() == priority);
assertTrue(thread3.getPriority() == priority);
}
@Test
void testPriority2() {
int priority = Thread.MIN_PRIORITY;
Thread.Builder builder = Thread.ofPlatform().priority(priority);
Thread thread1 = builder.unstarted(() -> { });
Thread thread2 = builder.start(() -> { });
Thread thread3 = builder.factory().newThread(() -> { });
assertTrue(thread1.getPriority() == priority);
assertTrue(thread2.getPriority() == priority);
assertTrue(thread3.getPriority() == priority);
}
@Test
void testPriority3() {
Thread currentThread = Thread.currentThread();
assumeFalse(currentThread.isVirtual(), "Main thread is a virtual thread");
int maxPriority = currentThread.getThreadGroup().getMaxPriority();
int priority = Math.min(maxPriority + 1, Thread.MAX_PRIORITY);
Thread.Builder builder = Thread.ofPlatform().priority(priority);
Thread thread1 = builder.unstarted(() -> { });
Thread thread2 = builder.start(() -> { });
Thread thread3 = builder.factory().newThread(() -> { });
assertTrue(thread1.getPriority() == priority);
assertTrue(thread2.getPriority() == priority);
assertTrue(thread3.getPriority() == priority);
}
@Test
void testPriority4() {
var builder = Thread.ofPlatform();
assertThrows(IllegalArgumentException.class,
() -> builder.priority(Thread.MIN_PRIORITY - 1));
}
@Test
void testPriority5() {
var builder = Thread.ofPlatform();
assertThrows(IllegalArgumentException.class,
() -> builder.priority(Thread.MAX_PRIORITY + 1));
}
/**
* Test Thread.Builder.OfPlatform.daemon.
*/
@Test
void testDaemon1() {
Thread.Builder builder = Thread.ofPlatform().daemon(false);
Thread thread1 = builder.unstarted(() -> { });
Thread thread2 = builder.start(() -> { });
Thread thread3 = builder.factory().newThread(() -> { });
assertFalse(thread1.isDaemon());
assertFalse(thread2.isDaemon());
assertFalse(thread3.isDaemon());
}
@Test
void testDaemon2() {
Thread.Builder builder = Thread.ofPlatform().daemon(true);
Thread thread1 = builder.unstarted(() -> { });
Thread thread2 = builder.start(() -> { });
Thread thread3 = builder.factory().newThread(() -> { });
assertTrue(thread1.isDaemon());
assertTrue(thread2.isDaemon());
assertTrue(thread3.isDaemon());
}
@Test
void testDaemon3() {
Thread.Builder builder = Thread.ofPlatform().daemon();
Thread thread1 = builder.unstarted(() -> { });
Thread thread2 = builder.start(() -> { });
Thread thread3 = builder.factory().newThread(() -> { });
assertTrue(thread1.isDaemon());
assertTrue(thread2.isDaemon());
assertTrue(thread3.isDaemon());
}
@Test
void testDaemon4() {
Thread.Builder builder = Thread.ofPlatform();
Thread thread1 = builder.unstarted(() -> { });
Thread thread2 = builder.start(() -> { });
Thread thread3 = builder.factory().newThread(() -> { });
// daemon status should be inherited
boolean d = Thread.currentThread().isDaemon();
assertTrue(thread1.isDaemon() == d);
assertTrue(thread2.isDaemon() == d);
assertTrue(thread3.isDaemon() == d);
}
/**
* Test Thread.ofVirtual creates daemon threads.
*/
@Test
void testDaemon5() {
Thread.Builder builder = Thread.ofVirtual();
Thread thread1 = builder.unstarted(() -> { });
Thread thread2 = builder.start(() -> { });
Thread thread3 = builder.factory().newThread(() -> { });
// daemon status should always be true
assertTrue(thread1.isDaemon());
assertTrue(thread2.isDaemon());
assertTrue(thread3.isDaemon());
}
/**
* Test Thread.Builder.OfPlatform.stackSize.
*/
@Test
void testStackSize1() {
Thread.Builder builder = Thread.ofPlatform().stackSize(1024*1024);
Thread thread1 = builder.unstarted(() -> { });
Thread thread2 = builder.start(() -> { });
Thread thread3 = builder.factory().newThread(() -> { });
}
@Test
void testStackSize2() {
Thread.Builder builder = Thread.ofPlatform().stackSize(0);
Thread thread1 = builder.unstarted(() -> { });
Thread thread2 = builder.start(() -> { });
Thread thread3 = builder.factory().newThread(() -> { });
}
@Test
void testStackSize3() {
var builder = Thread.ofPlatform();
assertThrows(IllegalArgumentException.class, () -> builder.stackSize(-1));
}
/**
* Test Thread.Builder.uncaughtExceptionHandler.
*/
@Test
void testUncaughtExceptionHandler1() throws Exception {
class FooException extends RuntimeException { }
AtomicReference<Thread> threadRef = new AtomicReference<>();
AtomicReference<Throwable> exceptionRef = new AtomicReference<>();
Thread thread = Thread.ofPlatform()
.uncaughtExceptionHandler((t, e) -> {
assertTrue(t == Thread.currentThread());
threadRef.set(t);
exceptionRef.set(e);
})
.start(() -> { throw new FooException(); });
thread.join();
assertTrue(threadRef.get() == thread);
assertTrue(exceptionRef.get() instanceof FooException);
}
@Test
void testUncaughtExceptionHandler2() throws Exception {
class FooException extends RuntimeException { }
AtomicReference<Thread> threadRef = new AtomicReference<>();
AtomicReference<Throwable> exceptionRef = new AtomicReference<>();
Thread thread = Thread.ofVirtual()
.uncaughtExceptionHandler((t, e) -> {
assertTrue(t == Thread.currentThread());
threadRef.set(t);
exceptionRef.set(e);
})
.start(() -> { throw new FooException(); });
thread.join();
assertTrue(threadRef.get() == thread);
assertTrue(exceptionRef.get() instanceof FooException);
}
@Test
void testUncaughtExceptionHandler3() throws Exception {
class FooException extends RuntimeException { }
AtomicReference<Thread> threadRef = new AtomicReference<>();
AtomicReference<Throwable> exceptionRef = new AtomicReference<>();
Thread thread = Thread.ofPlatform()
.uncaughtExceptionHandler((t, e) -> {
assertTrue(t == Thread.currentThread());
threadRef.set(t);
exceptionRef.set(e);
})
.factory()
.newThread(() -> { throw new FooException(); });
thread.start();
thread.join();
assertTrue(threadRef.get() == thread);
assertTrue(exceptionRef.get() instanceof FooException);
}
@Test
void testUncaughtExceptionHandler4() throws Exception {
class FooException extends RuntimeException { }
AtomicReference<Thread> threadRef = new AtomicReference<>();
AtomicReference<Throwable> exceptionRef = new AtomicReference<>();
Thread thread = Thread.ofPlatform()
.uncaughtExceptionHandler((t, e) -> {
assertTrue(t == Thread.currentThread());
threadRef.set(t);
exceptionRef.set(e);
})
.factory()
.newThread(() -> { throw new FooException(); });
thread.start();
thread.join();
assertTrue(threadRef.get() == thread);
assertTrue(exceptionRef.get() instanceof FooException);
}
static final ThreadLocal<Object> LOCAL = new ThreadLocal<>();
static final ThreadLocal<Object> INHERITED_LOCAL = new InheritableThreadLocal<>();
/**
* Tests that a builder creates threads that support thread locals
*/
private void testThreadLocals(Thread.Builder builder) throws Exception {
AtomicBoolean done = new AtomicBoolean();
Runnable task = () -> {
Object value = new Object();
LOCAL.set(value);
assertTrue(LOCAL.get() == value);
done.set(true);
};
done.set(false);
Thread thread1 = builder.unstarted(task);
thread1.start();
thread1.join();
assertTrue(done.get());
done.set(false);
Thread thread2 = builder.start(task);
thread2.join();
assertTrue(done.get());
done.set(false);
Thread thread3 = builder.factory().newThread(task);
thread3.start();
thread3.join();
assertTrue(done.get());
}
/**
* Tests that a builder creates threads that do not support thread locals
*/
private void testNoThreadLocals(Thread.Builder builder) throws Exception {
AtomicBoolean done = new AtomicBoolean();
Runnable task = () -> {
try {
LOCAL.set(new Object());
} catch (UnsupportedOperationException expected) {
done.set(true);
}
};
done.set(false);
Thread thread1 = builder.unstarted(task);
thread1.start();
thread1.join();
assertTrue(done.get());
done.set(false);
Thread thread2 = builder.start(task);
thread2.join();
assertTrue(done.get());
done.set(false);
Thread thread3 = builder.factory().newThread(task);
thread3.start();
thread3.join();
assertTrue(done.get());
}
/**
* Test Thread.Builder creates threads that allow thread locals.
*/
@Test
void testThreadLocals1() throws Exception {
Thread.Builder builder = Thread.ofPlatform();
testThreadLocals(builder);
}
@Test
void testThreadLocals2() throws Exception {
Thread.Builder builder = Thread.ofVirtual();
testThreadLocals(builder);
}
/**
* Tests that a builder creates threads that inherits the initial values of
* inheritable thread locals.
*/
private void testInheritedThreadLocals(Thread.Builder builder) throws Exception {
Object value = new Object();
INHERITED_LOCAL.set(value);
AtomicBoolean done = new AtomicBoolean();
Runnable task = () -> {
assertTrue(INHERITED_LOCAL.get() == value);
done.set(true);
};
done.set(false);
Thread thread1 = builder.unstarted(task);
thread1.start();
thread1.join();
assertTrue(done.get());
done.set(false);
Thread thread2 = builder.start(task);
thread2.join();
assertTrue(done.get());
done.set(false);
Thread thread3 = builder.factory().newThread(task);
thread3.start();
thread3.join();
assertTrue(done.get());
}
/**
* Tests that a builder creates threads that do not inherit the initial values
* of inheritable thread locals.
*/
private void testNoInheritedThreadLocals(Thread.Builder builder) throws Exception {
Object value = new Object();
INHERITED_LOCAL.set(value);
AtomicBoolean done = new AtomicBoolean();
Runnable task = () -> {
assertNull(INHERITED_LOCAL.get());
done.set(true);
};
done.set(false);
Thread thread1 = builder.unstarted(task);
thread1.start();
thread1.join();
assertTrue(done.get());
done.set(false);
Thread thread2 = builder.start(task);
thread2.join();
assertTrue(done.get());
done.set(false);
Thread thread3 = builder.factory().newThread(task);
thread3.start();
thread3.join();
assertTrue(done.get());
}
/**
* Test Thread.Builder creating threads that inherit or do not inherit
* the initial values of inheritable thread locals.
*/
@Test
void testInheritedThreadLocals1() throws Exception {
Thread.Builder builder = Thread.ofPlatform();
testInheritedThreadLocals(builder); // default
// do no inherit
builder.inheritInheritableThreadLocals(false);
testNoInheritedThreadLocals(builder);
// inherit
builder.inheritInheritableThreadLocals(true);
testInheritedThreadLocals(builder);
}
@Test
void testInheritedThreadLocals2() throws Exception {
Thread.Builder builder = Thread.ofVirtual();
testInheritedThreadLocals(builder); // default
// do no inherit
builder.inheritInheritableThreadLocals(false);
testNoInheritedThreadLocals(builder);
// inherit
builder.inheritInheritableThreadLocals(true);
testInheritedThreadLocals(builder);
}
/**
* Tests a builder creates threads that inherit the context class loader.
*/
private void testInheritContextClassLoader(Thread.Builder builder) throws Exception {
ClassLoader savedCCL = Thread.currentThread().getContextClassLoader();
try {
ClassLoader loader = new ClassLoader() { };
Thread.currentThread().setContextClassLoader(loader);
var ref = new AtomicReference<ClassLoader>();
Runnable task = () -> {
ref.set(Thread.currentThread().getContextClassLoader());
};
// unstarted
Thread thread1 = builder.unstarted(task);
assertTrue(thread1.getContextClassLoader() == loader);
// started
ref.set(null);
thread1.start();
thread1.join();
assertTrue(ref.get() == loader);
// factory
Thread thread2 = builder.factory().newThread(task);
assertTrue(thread2.getContextClassLoader() == loader);
// started
ref.set(null);
thread2.start();
thread2.join();
assertTrue(ref.get() == loader);
} finally {
Thread.currentThread().setContextClassLoader(savedCCL);
}
}
/**
* Tests a builder creates threads does not inherit the context class loader.
*/
private void testNoInheritContextClassLoader(Thread.Builder builder) throws Exception {
ClassLoader savedCCL = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader( new ClassLoader() { });
ClassLoader scl = ClassLoader.getSystemClassLoader();
var ref = new AtomicReference<ClassLoader>();
Runnable task = () -> {
ref.set(Thread.currentThread().getContextClassLoader());
};
// unstarted
Thread thread1 = builder.unstarted(task);
assertTrue(thread1.getContextClassLoader() == scl);
// started
ref.set(null);
thread1.start();
thread1.join();
assertTrue(ref.get() == scl);
// factory
Thread thread2 = builder.factory().newThread(task);
assertTrue(thread2.getContextClassLoader() == scl);
// started
ref.set(null);
thread2.start();
thread2.join();
assertTrue(ref.get() == scl);
} finally {
Thread.currentThread().setContextClassLoader(savedCCL);
}
}
/**
* Test Thread.Builder creating threads that inherit or do not inherit
* the thread context class loader.
*/
@Test
void testContextClassLoader1() throws Exception {
Thread.Builder builder = Thread.ofPlatform();
testInheritContextClassLoader(builder); // default
// do no inherit
builder.inheritInheritableThreadLocals(false);
testNoInheritContextClassLoader(builder);
// inherit
builder.inheritInheritableThreadLocals(true);
testInheritContextClassLoader(builder);
}
@Test
void testContextClassLoader2() throws Exception {
Thread.Builder builder = Thread.ofVirtual();
testInheritContextClassLoader(builder); // default
// do no inherit
builder.inheritInheritableThreadLocals(false);
testNoInheritContextClassLoader(builder);
// inherit
builder.inheritInheritableThreadLocals(true);
testInheritContextClassLoader(builder);
}
/**
* Test NullPointerException.
*/
@Test
void testNulls1() {
Thread.Builder.OfPlatform builder = Thread.ofPlatform();
assertThrows(NullPointerException.class, () -> builder.group(null));
assertThrows(NullPointerException.class, () -> builder.name(null));
assertThrows(NullPointerException.class, () -> builder.name(null, 0));
assertThrows(NullPointerException.class, () -> builder.uncaughtExceptionHandler(null));
assertThrows(NullPointerException.class, () -> builder.unstarted(null));
assertThrows(NullPointerException.class, () -> builder.start(null));
}
@Test
void testNulls2() {
Thread.Builder builder = Thread.ofVirtual();
assertThrows(NullPointerException.class, () -> builder.name(null));
assertThrows(NullPointerException.class, () -> builder.name(null, 0));
assertThrows(NullPointerException.class, () -> builder.uncaughtExceptionHandler(null));
assertThrows(NullPointerException.class, () -> builder.unstarted(null));
assertThrows(NullPointerException.class, () -> builder.start(null));
}
}