diff a/src/java.base/linux/classes/sun/nio/ch/EPollSelectorImpl.java b/src/java.base/linux/classes/sun/nio/ch/EPollSelectorImpl.java
--- a/src/java.base/linux/classes/sun/nio/ch/EPollSelectorImpl.java
+++ b/src/java.base/linux/classes/sun/nio/ch/EPollSelectorImpl.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2022, 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.  Oracle designates this
@@ -33,11 +33,10 @@
 import java.util.Deque;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Consumer;
-import jdk.internal.misc.Blocker;
 
 import static sun.nio.ch.EPoll.EPOLLIN;
 import static sun.nio.ch.EPoll.EPOLL_CTL_ADD;
 import static sun.nio.ch.EPoll.EPOLL_CTL_DEL;
 import static sun.nio.ch.EPoll.EPOLL_CTL_MOD;
@@ -103,40 +102,75 @@
         boolean timedPoll = (to > 0);
 
         int numEntries;
         processUpdateQueue();
         processDeregisterQueue();
-        try {
-            begin(blocking);
 
-            do {
-                long startTime = timedPoll ? System.nanoTime() : 0;
-                boolean attempted = Blocker.begin(blocking);
-                try {
+        if (Thread.currentThread().isVirtual()) {
+            numEntries = (timedPoll)
+                    ? timedPoll(TimeUnit.MILLISECONDS.toNanos(to))
+                    : untimedPoll(blocking);
+        } else {
+            try {
+                begin(blocking);
+                do {
+                    long startTime = timedPoll ? System.nanoTime() : 0;
                     numEntries = EPoll.wait(epfd, pollArrayAddress, NUM_EPOLLEVENTS, to);
-                } finally {
-                    Blocker.end(attempted);
-                }
-                if (numEntries == IOStatus.INTERRUPTED && timedPoll) {
-                    // timed poll interrupted so need to adjust timeout
-                    long adjust = System.nanoTime() - startTime;
-                    to -= (int) TimeUnit.NANOSECONDS.toMillis(adjust);
-                    if (to <= 0) {
-                        // timeout expired so no retry
-                        numEntries = 0;
+                    if (numEntries == IOStatus.INTERRUPTED && timedPoll) {
+                        // timed poll interrupted so need to adjust timeout
+                        long adjust = System.nanoTime() - startTime;
+                        to -= (int) TimeUnit.NANOSECONDS.toMillis(adjust);
+                        if (to <= 0) {
+                            // timeout expired so no retry
+                            numEntries = 0;
+                        }
                     }
-                }
-            } while (numEntries == IOStatus.INTERRUPTED);
-            assert IOStatus.check(numEntries);
-
-        } finally {
-            end(blocking);
+                } while (numEntries == IOStatus.INTERRUPTED);
+            } finally {
+                end(blocking);
+            }
         }
+        assert IOStatus.check(numEntries);
+
         processDeregisterQueue();
         return processEvents(numEntries, action);
     }
 
+    /**
+     * If blocking, parks the current virtual thread until a file descriptor is polled
+     * or the thread is interrupted.
+     */
+    private int untimedPoll(boolean block) throws IOException {
+        int numEntries = EPoll.wait(epfd, pollArrayAddress, NUM_EPOLLEVENTS, 0);
+        if (block) {
+            while (numEntries == 0 && !Thread.currentThread().isInterrupted()) {
+                Poller.pollSelector(epfd, 0);
+                numEntries = EPoll.wait(epfd, pollArrayAddress, NUM_EPOLLEVENTS, 0);
+            }
+        }
+        return numEntries;
+    }
+
+    /**
+     * Parks the current virtual thread until a file descriptor is polled, or the thread
+     * is interrupted, for up to the specified waiting time.
+     */
+    private int timedPoll(long nanos) throws IOException {
+        long startNanos = System.nanoTime();
+        int numEntries = EPoll.wait(epfd, pollArrayAddress, NUM_EPOLLEVENTS, 0);
+        while (numEntries == 0 && !Thread.currentThread().isInterrupted()) {
+            long remainingNanos = nanos - (System.nanoTime() - startNanos);
+            if (remainingNanos <= 0) {
+                // timeout
+                break;
+            }
+            Poller.pollSelector(epfd, remainingNanos);
+            numEntries = EPoll.wait(epfd, pollArrayAddress, NUM_EPOLLEVENTS, 0);
+        }
+        return numEntries;
+    }
+
     /**
      * Process changes to the interest ops.
      */
     private void processUpdateQueue() {
         assert Thread.holdsLock(this);
