杰瑞科技汇

Java线程new后,如何正确启动与运行?

Of course! Creating a new thread in Java is a fundamental concept. Let's break it down, from the classic, traditional ways to the modern, recommended approaches.

The Core Concept

In Java, a thread is the smallest unit of a process. It represents a separate path of execution within your program. By creating multiple threads, you can perform several tasks concurrently (or in parallel, depending on the number of CPU cores).


The Classic Ways (Pre-Java 8)

These are the original methods for creating threads. They are still valid and you will see them in older codebases, but they are generally not the preferred approach for new code today.

Method 1: Extending the Thread Class

You create a new class that extends java.lang.Thread and override its run() method. The run() method contains the code that the thread will execute.

Steps:

  1. Create a class that extends Thread.
  2. Override the run() method.
  3. Create an instance of your new class.
  4. Call the start() method on the instance. (This is crucial! Calling run() directly will just execute the code in the current thread, not a new one.)

Example:

// Step 1: Create a class that extends Thread
class MyThread extends Thread {
    // Step 2: Override the run() method
    @Override
    public void run() {
        // Code to be executed by the new thread
        for (int i = 1; i <= 5; i++) {
            System.out.println("MyThread: " + i);
            try {
                // Sleep for a bit to simulate work
                Thread.sleep(500); // 500 milliseconds
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class ThreadExample {
    public static void main(String[] args) {
        // Step 3: Create an instance of the thread
        MyThread thread1 = new MyThread();
        // Step 4: Start the thread
        thread1.start();
        // The main thread continues its execution
        for (int i = 1; i <= 5; i++) {
            System.out.println("Main Thread: " + i);
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

Pros:

  • Simple and straightforward for basic tasks.

Cons:

  • Poor OOP Design: Your class is now a Thread. What if your class also needs to extend another class? Java doesn't support multiple inheritance, so you can't.
  • Tight Coupling: Your task (the code in run()) is tightly coupled to the Thread class itself.

Method 2: Implementing the Runnable Interface (More Flexible)

This is a more common and flexible approach. You create a class that implements the java.lang.Runnable interface. The Runnable interface is just a "task" that can be executed by a thread.

Steps:

  1. Create a class that implements Runnable.
  2. Implement the run() method.
  3. Create an instance of your Runnable class.
  4. Create a Thread object, passing your Runnable instance to its constructor.
  5. Call start() on the Thread object.

Example:

// Step 1: Create a class that implements Runnable
class MyRunnableTask implements Runnable {
    // Step 2: Implement the run() method
    @Override
    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.println("MyRunnable: " + i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class RunnableExample {
    public static void main(String[] args) {
        // Step 3: Create an instance of the task
        MyRunnableTask myTask = new MyRunnableTask();
        // Step 4: Create a Thread, passing the task to it
        Thread thread2 = new Thread(myTask);
        // Step 5: Start the thread
        thread2.start();
        // Main thread continues
        for (int i = 1; i <= 5; i++) {
            System.out.println("Main Thread: " + i);
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

Pros:

  • Better OOP Design: Your class is no longer a Thread; it's just a Runnable task. It can still extend another class.
  • Separation of Concerns: It cleanly separates the "what to do" (the Runnable task) from the "how to do it" (the Thread that executes it).

Cons:

  • Slightly more boilerplate code (you need to create a Thread object separately).

The Modern Way (Java 8 and Later)

Java 8 introduced Lambdas, which made the Runnable approach much cleaner and more concise. This is now the recommended standard for most new code.

You can pass a lambda expression directly to the Thread constructor. A lambda is a short, anonymous block of code that implements a functional interface (like Runnable).

Example:

public class LambdaExample {
    public static void main(String[] args) {
        // Create a Thread and pass a lambda expression that implements the run() method
        Thread thread3 = new Thread(() -> {
            for (int i = 1; i <= 5; i++) {
                System.out.println("Lambda Thread: " + i);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread3.start();
        // Main thread continues
        for (int i = 1; i <= 5; i++) {
            System.out.println("Main Thread: " + i);
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

Why is this better?

  • Conciseness: No need to create a separate class. The task is defined right where it's needed.
  • Readability: It's very clear that you're defining a task to be run on a new thread.
  • Modern Java Practice: This aligns with functional programming paradigms common in modern Java development.

The Enterprise Way: The ExecutorService Framework

For any real-world application, manually creating and managing Thread objects is not recommended. It's inefficient and error-prone. You should use the Executor Framework, which is part of java.util.concurrent.

The ExecutorService manages a pool of threads for you. This approach provides significant benefits:

  • Thread Reuse: It avoids the overhead of creating and destroying threads for every task.
  • Resource Management: It prevents you from overwhelming the system by creating too many threads.
  • Lifecycle Management: It provides clean ways to shut down the thread pool gracefully.
  • Advanced Features: It supports features like scheduled execution, futures, and more.

Example:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class ExecutorServiceExample {
    public static void main(String[] args) {
        // Create a thread pool with 2 threads
        ExecutorService executor = Executors.newFixedThreadPool(2);
        // Submit tasks to the executor
        executor.submit(() -> {
            for (int i = 1; i <= 5; i++) {
                System.out.println("Task 1: " + i);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        executor.submit(() -> {
            for (int i = 1; i <= 5; i++) {
                System.out.println("Task 2: " + i);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        // Shut down the executor. It will not accept new tasks.
        executor.shutdown();
        try {
            // Wait a maximum of 1 second for existing tasks to terminate
            if (!executor.awaitTermination(1, TimeUnit.SECONDS)) {
                // Forcefully shutdown if tasks are still running
                executor.shutdownNow();
            }
        } catch (InterruptedException e) {
            executor.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }
}

Summary and Recommendation

Method When to Use Pros Cons
Extends Thread Almost never. Only for very simple, self-contained examples. Simple for beginners. Poor OOP design, tight coupling.
Implements Runnable Good for reusable tasks in Java 7 and earlier. Flexible, good OOP design. More boilerplate code.
Lambda Expression Recommended for most new code. Use when you need a simple, one-off task. Concise, readable, modern. Still requires manual thread management for the task itself.
ExecutorService The standard for all production applications. Use for any non-trivial, concurrent work. Manages thread pools, efficient, scalable, robust. Slightly more complex to set up.

Final Recommendation:

  • For learning, start with the Runnable interface to understand the concept.
  • For any practical, modern Java code, use Lambda Expressions with the ExecutorService framework. This gives you the best of both worlds: the conciseness of lambdas and the power and safety of a managed thread pool.
分享:
扫描分享到社交APP
上一篇
下一篇