diff a/src/java.base/share/classes/java/util/concurrent/StructuredTaskScope.java b/src/java.base/share/classes/java/util/concurrent/StructuredTaskScope.java
--- a/src/java.base/share/classes/java/util/concurrent/StructuredTaskScope.java
+++ b/src/java.base/share/classes/java/util/concurrent/StructuredTaskScope.java
@@ -310,13 +310,12 @@
     private static final int CLOSED   = 2;
 
     // state: set to SHUTDOWN by any thread, set to CLOSED by owner, read by any thread
     private volatile int state;
 
-    // Counters to support checking that the task scope owner joins before processing
-    // results and attempts join before closing the task scope. These counters are
-    // accessed only by the owner thread.
+    // Counters to support checking that the task scope owner joins before closing the task
+    // scope. These counters are accessed only by the owner thread.
     private int forkRound;         // incremented when the first subtask is forked after join
     private int lastJoinAttempted; // set to the current fork round when join is attempted
     private int lastJoinCompleted; // set to the current fork round when join completes
 
     /**
@@ -368,32 +367,22 @@
         State state();
 
         /**
          * Returns the result of the subtask.
          *
-         * <p> To ensure correct usage, if the scope owner {@linkplain #fork(Callable) forks}
-         * a subtask, then it must join (with {@link #join() join} or {@link #joinUntil(Instant)
-         * joinUntil}) before it can obtain the result of the subtask.
-         *
          * @return the possibly-null result
-         * @throws IllegalStateException if the subtask has not completed, did not complete
-         * successfully, or the current thread is the task scope owner and did not join
-         * after forking
+         * @throws IllegalStateException if the subtask has not completed or did not
+         * complete successfully
          * @see State#SUCCESS
          */
         T get();
 
         /**
          * {@return the exception thrown by the subtask}
          *
-         * <p> To ensure correct usage, if the scope owner {@linkplain #fork(Callable) forks}
-         * a subtask, then it must join (with {@link #join() join} or {@link #joinUntil(Instant)
-         * joinUntil}) before it can obtain the exception thrown by the subtask.
-         *
-         * @throws IllegalStateException if the subtask has not completed, completed with
-         * a result, or the current thread is the task scope owner and did not join after
-         * forking
+         * @throws IllegalStateException if the subtask has not completed or completed
+         * with a result rather than an exception
          * @see State#FAILED
          */
         Throwable exception();
     }
 
@@ -467,20 +456,10 @@
         Thread currentThread = Thread.currentThread();
         if (currentThread != flock.owner() && !flock.containsThread(currentThread))
             throw new WrongThreadException("Current thread not owner or thread in the tree");
     }
 
-    /**
-     * Throws IllegalStateException if the current thread is the owner, and the owner did
-     * not join after forking a subtask in the given fork round.
-     */
-    private void ensureJoinedIfOwner(int round) {
-        if (Thread.currentThread() == flock.owner() && (round > lastJoinCompleted)) {
-            throw newIllegalStateExceptionNoJoin();
-        }
-    }
-
     /**
      * Ensures that the current thread is the owner of this task scope and that it joined
      * (with {@link #join()} or {@link #joinUntil(Instant)}) after {@linkplain #fork(Callable)
      * forking} subtasks.
      *
@@ -574,11 +553,11 @@
                 // new round if first fork after join
                 round++;
             }
         }
 
-        SubtaskImpl<U> subtask = new SubtaskImpl<>(this, task, round);
+        var subtask = new SubtaskImpl<U>(this, task);
         if (s < SHUTDOWN) {
             // create thread to run task
             Thread thread = factory.newThread(subtask);
             if (thread == null) {
                 throw new RejectedExecutionException("Rejected by thread factory");
@@ -842,11 +821,11 @@
             state = CLOSED;
         }
 
         // throw ISE if the owner didn't attempt to join after forking
         if (forkRound > lastJoinAttempted) {
-            lastJoinCompleted = forkRound;
+            lastJoinCompleted = forkRound;  // ensureOwnerAndJoined is a no-op after close
             throw newIllegalStateExceptionNoJoin();
         }
     }
 
     @Override
@@ -872,19 +851,15 @@
             }
         }
 
         private final StructuredTaskScope<? super T> scope;
         private final Callable<? extends T> task;
-        private final int round;
         private volatile Object result;
 
-        SubtaskImpl(StructuredTaskScope<? super T> scope,
-                    Callable<? extends T> task,
-                    int round) {
+        SubtaskImpl(StructuredTaskScope<? super T> scope, Callable<? extends T> task) {
             this.scope = scope;
             this.task = task;
-            this.round = round;
         }
 
         @Override
         public void run() {
             T result = null;
@@ -926,11 +901,10 @@
             }
         }
 
         @Override
         public T get() {
-            scope.ensureJoinedIfOwner(round);
             Object result = this.result;
             if (result instanceof AltResult) {
                 if (result == RESULT_NULL) return null;
             } else if (result != null) {
                 @SuppressWarnings("unchecked")
@@ -941,11 +915,10 @@
                     "Result is unavailable or subtask did not complete successfully");
         }
 
         @Override
         public Throwable exception() {
-            scope.ensureJoinedIfOwner(round);
             Object result = this.result;
             if (result instanceof AltResult alt && alt.state() == State.FAILED) {
                 return alt.exception();
             }
             throw new IllegalStateException(
