在 Java SE 中,多线程是并发编程的核心,能够有效地提升程序性能,特别是在 CPU 密集型或 I/O 密集型的应用中。Java 为开发者提供了强大的多线程支持,包括 Thread 类和 Runnable 接口。多线程的实现可以让多个任务同时进行,从而提高系统的效率。

1. 线程的基本概念

线程(Thread) 是操作系统能够进行运算调度的最小单位。每个线程都有自己独立的执行路径,程序中可能同时有多个线程并行执行。Java 中的线程可以通过 Thread 类或 Runnable 接口来创建和控制。

线程的状态

Java 中的线程有五种基本状态:

  1. 新建(New):线程对象被创建,但尚未开始执行。
  2. 就绪(Runnable):线程已准备好并等待操作系统的调度来执行。
  3. 运行(Running):线程正在执行任务。
  4. 阻塞(Blocked):线程被阻塞,等待某个资源的可用(例如 I/O 操作)。
  5. 死亡(Terminated):线程执行完毕或被终止。

2. 创建线程的两种方式

2.1 继承 Thread 类

Thread 类是 Java 中用于创建线程的基本类。通过继承 Thread 类并重写其 run 方法来定义线程执行的任务。

示例:

class MyThread extends Thread {
    @Override
    public void run() {
        // 执行任务
        System.out.println("Thread is running");
    }

    public static void main(String[] args) {
        MyThread thread = new MyThread(); // 创建线程对象
        thread.start(); // 启动线程
    }
}

解释:

  • MyThread 类继承了 Thread 类,并重写了 run() 方法,run() 方法中包含了线程需要执行的任务。
  • start() 方法用于启动线程,内部会调用 run() 方法。

2.2 实现 Runnable 接口

如果类已经继承了其他类,不能再继承 Thread 类,这时我们可以通过实现 Runnable 接口来实现线程。Runnable 接口中的 run() 方法定义了线程执行的任务。

示例:

class MyRunnable implements Runnable {
    @Override
    public void run() {
        // 执行任务
        System.out.println("Runnable thread is running");
    }

    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable); // 创建线程对象
        thread.start(); // 启动线程
    }
}

解释:

  • MyRunnable 实现了 Runnable 接口,并重写了 run() 方法。
  • Thread 类接收一个 Runnable 对象并启动执行。

3. 线程的生命周期

线程的生命周期从新建到终止经过多个状态。

  1. 新建状态:当线程对象被创建后,处于新建状态,此时并未开始执行。
  2. 就绪状态:当调用 start() 方法时,线程进入就绪状态,等待操作系统的调度。
  3. 运行状态:当操作系统分配到 CPU 时间片时,线程进入运行状态,开始执行 run() 方法中的任务。
  4. 阻塞状态:线程可能因为 I/O 操作或等待资源而进入阻塞状态。
  5. 死亡状态:线程执行完毕或被终止时,进入死亡状态,生命周期结束。

4. 常见的线程方法

Java Thread 类提供了一些常用方法来控制线程的行为:

  • start():启动线程,调用线程的 run() 方法。
  • run():定义线程的执行任务。
  • sleep(long millis):使当前线程休眠指定的毫秒数,暂停执行。
  • yield():使当前线程暂停执行,调度其他线程。
  • join():使当前线程等待,直到目标线程执行完毕。
  • interrupt():中断当前线程的执行。

示例:

class MyThread extends Thread {
    @Override
    public void run() {
        try {
            // 线程休眠 2 秒
            System.out.println("Thread started...");
            Thread.sleep(2000);
            System.out.println("Thread resumed...");
        } catch (InterruptedException e) {
            System.out.println("Thread interrupted");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MyThread thread = new MyThread();
        thread.start();  // 启动线程
        
        thread.join();   // 等待线程执行完毕
        System.out.println("Main thread finished");
    }
}

解释:

  • sleep(2000) 使线程暂停执行 2 秒钟。
  • join() 方法使主线程等待 MyThread 完成后再执行。

5. 线程同步

在多线程编程中,多个线程可能会共享某些资源,如果不加以控制,就会导致数据不一致的问题。同步(synchronization) 机制可以避免这种情况。

5.1 使用 synchronized 关键字

synchronized 关键字用于方法或代码块,可以确保在同一时刻只有一个线程访问该资源。

示例:

class Counter {
    private int count = 0;

    // 使用 synchronized 保证线程安全
    public synchronized void increment() {
        count++;
    }

    public int getCount() {
        return count;
    }
}

public class SyncExample {
    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();
        
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        });

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        });

        t1.start();
        t2.start();

        t1.join();
        t2.join();
        
        System.out.println("Final count: " + counter.getCount()); // 输出: 2000
    }
}

解释:

  • increment() 方法使用了 synchronized 关键字,确保同一时刻只有一个线程能够访问它。
  • 通过 join() 确保两个线程执行完毕后再输出最终结果。

5.2 同步代码块

如果只需要同步某一段代码,可以使用同步代码块,避免整个方法被锁住,提高程序效率。

示例:

class Counter {
    private int count = 0;

    public void increment() {
        synchronized (this) {
            count++;
        }
    }

    public int getCount() {
        return count;
    }
}

6. 线程池

线程池是 Java 中用来管理线程的一种方式,能有效地复用线程、控制线程数量、提高效率。Java 提供了 Executor 框架来实现线程池。

6.1 创建线程池

使用 Executors 工厂类来创建常见的线程池:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolExample {
    public static void main(String[] args) {
        // 创建一个固定大小的线程池
        ExecutorService pool = Executors.newFixedThreadPool(3);

        // 提交任务到线程池
        pool.submit(() -> {
            System.out.println("Task 1");
        });
        pool.submit(() -> {
            System.out.println("Task 2");
        });
        pool.submit(() -> {
            System.out.println("Task 3");
        });

        pool.shutdown(); // 关闭线程池
    }
}

解释:

  • newFixedThreadPool(3) 创建了一个包含 3 个线程的线程池。
  • submit() 方法用于提交任务,任务将在空闲线程中执行。
  • 使用 shutdown() 方法来关闭线程池。

7. 总结

Java SE 提供了丰富的多线程支持,通过 Thread 类和 Runnable 接口可以方便地实现多线程。为了实现线程安全,可以使用 synchronized 关键字来避免资源的竞态条件。而线程池则提供了一种高效的线程管理方式,适合于处理大量短期的任务。

  • 创建线程:可以通过继承 Thread 类或实现 Runnable 接口来创建线程。
  • 线程同步synchronized 关键字可以用于方法和代码块,确保线程安全。
  • 线程池:线程池管理可以提高性能,并避免线程的频繁创建与销毁。

多线程是 Java 中非常重要的编程概念,掌握了多线程编程后,可以处理复杂的并发任务,提高系统的性能。