Question 1
Which two statements about virtual threads in Java 21 are correct? (Choose two.)
A. Calling setPriority(Thread.MAX_PRIORITY) on a virtual thread has no effect; getPriority() still returns 5Correct answer
Virtual threads are scheduled by a FIFO ForkJoinPool with no notion of Java thread priority, so setPriority is specified to do nothing and getPriority() always returns NORM_PRIORITY (5).
B. A virtual thread's isVirtual() returns false until the thread is first mounted on a carrier thread
Confuses being a virtual thread with being mounted. Virtual-ness is a property of the object from construction; an unstarted virtual thread in state NEW already reports isVirtual() == true.
C. A class that extends Thread is always a platform thread; there is no public virtual Thread subclass you can extendCorrect answer
The implementation class java.lang.VirtualThread is package-private and final, and every public Thread constructor creates a platform thread; virtual threads come only from ofVirtual(), startVirtualThread(...), a virtual ThreadFactory, or newVirtualThreadPerTaskExecutor().
D. Calling Thread.sleep(...) inside a virtual thread blocks its carrier platform thread for the whole sleep duration
Inverts the feature's central point. Thread.sleep is a blocking operation that UNMOUNTS the virtual thread, releasing the carrier to run other virtual threads — which is exactly why blocking is cheap.
Explanation
Why `Calling setPriority(Thread.MAX_PRIORITY) on a virtual thread has...` is correct: virtual threads are scheduled by a FIFO ForkJoinPool scheduler that has no notion of Java thread priority, so `Thread.setPriority` is specified to do nothing on a virtual thread and `getPriority()` always answers `Thread.NORM_PRIORITY`, which is `5`. Compare a platform thread, where the same call really does move the value to `10`. Why `A class that extends Thread is always a platform thread; there is...` is correct: the implementation class is `java.lang.VirtualThread`, which is package-private and final, and every public `Thread` constructor creates a platform thread. So `new Thread(r) { }` reports `isVirtual() == false`. Virtual threads can only be obtained from `Thread.ofVirtual()`, `Thread.startVirtualThread(...)`, a virtual `ThreadFactory`, or `Executors.newVirtualThreadPerTaskExecutor()` — never by subclassing. Why the others are wrong: `A virtual thread's isVirtual() returns false until the thread is first...` confuses *being a virtual thread* with *being mounted*. Virtual-ness is a property of the object from construction: an unstarted virtual thread in state `NEW` already reports `isVirtual() == true`. Mounting is a scheduling event, not an identity change. `Calling Thread.sleep(...) inside a virtual thread blocks its carrier...` inverts the central point of the feature. `Thread.sleep` is one of the blocking operations that *unmounts* a virtual thread, releasing the carrier to run other virtual threads; that is exactly why blocking is cheap. The belief it encodes — that a virtual thread holds its carrier while blocked — would make virtual threads no better than a fixed pool. Exam tip: virtual threads deliberately drop the platform-thread knobs. Priority is ignored, `setDaemon(false)` is rejected (they are always daemon), thread groups are fixed, and `Thread.Builder.OfVirtual` offers no `priority()`, `stackSize()`, `group()` or `daemon()` method at all — those live only on `Thread.Builder.OfPlatform`.