杰瑞科技汇

Java thread.sleep为何会阻塞线程?

Of course! Let's break down Thread.sleep() in Java, from the basics to best practices and common pitfalls.

Java thread.sleep为何会阻塞线程?-图1
(图片来源网络,侵删)

What is Thread.sleep()?

Thread.sleep() is a built-in Java method that pauses the current thread's execution for a specified period of time.

Think of it like telling a chef, "Stop cooking for 10 minutes." The chef (the thread) will stop what they are doing and wait. Other chefs (other threads) can continue working during that time.


The Basic Syntax

The method is part of the java.lang.Thread class. It has two overloaded versions:

  1. Thread.sleep(long milliseconds)
  2. Thread.sleep(long milliseconds, int nanoseconds)

The most common one is the first, which takes the duration in milliseconds.

Java thread.sleep为何会阻塞线程?-图2
(图片来源网络,侵删)
// Pauses the current thread for 1000 milliseconds (which is 1 second)
Thread.sleep(1000); 

Key Points:

  • It's a static method, so you call it on the Thread class itself (Thread.sleep(...)), not on an instance of a thread.
  • It always puts the current executing thread to sleep.
  • It throws a checked exception, InterruptedException, so you must handle it with a try-catch block.

A Simple Example

Here is a complete, runnable example that demonstrates Thread.sleep() in action.

public class SleepExample {
    public static void main(String[] args) {
        System.out.println("Starting the countdown...");
        for (int i = 5; i > 0; i--) {
            try {
                // Print the current number
                System.out.println(i);
                // Pause the main thread for 1 second (1000 milliseconds)
                Thread.sleep(1000); 
            } catch (InterruptedException e) {
                // This block will execute if another thread interrupts this one while it's sleeping
                System.out.println("The countdown was interrupted!");
                // We should restore the interrupted status of the thread
                Thread.currentThread().interrupt();
                return; // Exit the loop
            }
        }
        System.out.println("Liftoff!");
    }
}

Output:

Starting the countdown...
5
4
3
2
1
Liftoff!

(Notice the one-second delay between each number being printed.)

Java thread.sleep为何会阻塞线程?-图3
(图片来源网络,侵删)

The InterruptedException

This is one of the most important concepts to understand about Thread.sleep().

  • What it is: InterruptedException is a special exception. It's a way for one thread to politely ask another thread to stop what it's doing.
  • When it's thrown: If a thread is sleeping (i.e., inside Thread.sleep()) and another thread calls the interrupt() method on it, the sleeping thread will "wake up" immediately and throw InterruptedException.
  • Best Practice for Handling it: You should never swallow an InterruptedException by just catching it and doing nothing. This breaks the contract of interruption. The correct way is to:
    1. Catch the exception.
    2. Optionally, perform some cleanup or logging.
    3. Restore the thread's "interrupted status" by calling Thread.currentThread().interrupt();. This is crucial because it allows higher-level code to know that the thread was interrupted.

The example above shows the recommended way to handle it.


Common Use Cases for Thread.sleep()

While Thread.sleep() seems simple, it has several practical uses.

Simulating Delays

This is the most common use, especially in testing or when building simple prototypes.

// Simulate a network call that takes 2 seconds
System.out.println("Calling the server...");
Thread.sleep(2000); // Pretend the call takes 2 seconds
System.out.println("Server responded!");

Polling (Use with Caution!)

Sometimes you need to wait for a condition to become true. A naive way is to poll it in a loop.

// WARNING: This is a simple but often inefficient way to poll.
while (!isDataReady()) {
    try {
        // Check every 100 milliseconds
        Thread.sleep(100); 
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        break;
    }
}
System.out.println("Data is ready!");

Better Alternative: For production code, using constructs like wait(), notify(), or modern concurrency tools (CountDownLatch, CompletableFuture) is almost always better than polling with sleep().

Rate Limiting

If you are making repeated calls to an external API, you might be limited to a certain number of requests per second. sleep() can help you respect that limit.

int requestsPerSecond = 5;
long delay = 1000 / requestsPerSecond; // 200 ms between requests
for (int i = 0; i < 20; i++) {
    makeApiCall();
    try {
        Thread.sleep(delay);
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        break;
    }
}

Important Caveats and Best Practices

It's Not Precise

Thread.sleep() is a hint to the operating system, not a precise timer. The actual duration the thread sleeps can be longer than you specify due to:

  • OS Scheduling: The OS might not schedule your thread to run again immediately after the sleep time expires.
  • System Load: If the system is very busy, your thread might have to wait longer.

It Does Not Release Locks

This is a critical point. If a thread is sleeping inside a synchronized block, it still holds the lock on that object. No other thread can enter that block until the sleeping thread wakes up and releases the lock.

public class LockExample {
    private final Object lock = new Object();
    public void doSomething() {
        synchronized (lock) {
            System.out.println("Thread " + Thread.currentThread().getName() + " acquired the lock.");
            try {
                System.out.println("Thread " + Thread.currentThread().getName() + " is going to sleep...");
                Thread.sleep(3000); // This thread holds the lock for 3 seconds!
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            System.out.println("Thread " + Thread.currentThread().getName() + " woke up and is releasing the lock.");
        }
    }
    public static void main(String[] args) throws InterruptedException {
        LockExample example = new LockExample();
        Thread t1 = new Thread(example::doSomething, "Thread-1");
        Thread t2 = new Thread(example::doSomething, "Thread-2");
        t1.start();
        t2.start();
    }
}

Output:

Thread Thread-1 acquired the lock.
Thread Thread-1 is going to sleep...
// (3-second pause)
Thread Thread-1 woke up and is releasing the lock.
Thread Thread-2 acquired the lock.
Thread Thread-2 is going to sleep...
// (another 3-second pause)
Thread Thread-2 woke up and is releasing the lock.

Notice how Thread-2 had to wait for Thread-1 to finish its sleep before it could acquire the lock.

When to Avoid Thread.sleep()

  • For High-Precision Timing: For tasks that need millisecond or microsecond precision, use System.nanoTime() and a more advanced scheduler, not Thread.sleep().
  • As a Replacement for Proper Concurrency: Using sleep() to solve race conditions or synchronization problems is a code smell. It hides the real issue and makes your code unreliable. Use proper synchronization tools (synchronized, ReentrantLock, Semaphore, etc.) instead.

Modern Alternatives (Java 5+)

For more sophisticated timing and scheduling, the java.util.concurrent package offers better tools.

  • ScheduledExecutorService: This is the modern, preferred way to schedule tasks to run after a delay or periodically. It's more flexible and powerful than Thread.sleep().
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ScheduledExecutorExample {
    public static void main(String[] args) {
        ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
        // Schedule a task to run after 2 seconds
        scheduler.schedule(() -> {
            System.out.println("This task was executed after a 2-second delay.");
        }, 2, TimeUnit.SECONDS);
        // Schedule a task to run repeatedly every 1 second
        scheduler.scheduleAtFixedRate(() -> {
            System.out.println("This task runs every second.");
        }, 0, 1, TimeUnit.SECONDS);
        // Let it run for a bit then shut down
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        scheduler.shutdown();
    }
}

Summary

Feature Description
What it is A static method to pause the current thread.
Precision Not precise. It's a hint to the OS.
Locks Does not release any locks it holds.
Exception Throws InterruptedException if the thread is interrupted.
Use Case Good for simple delays, simulation, and basic polling.
Avoid for High-precision timing and as a concurrency control mechanism.
Modern Alternative ScheduledExecutorService for more robust scheduling.
分享:
扫描分享到社交APP
上一篇
下一篇