多线程及其声明周期

多线程

  • 多线程 是指在一个进程中同时运行多个线程,每个线程执行不同的任务
  • 在多线程中,主线程也是一个单独的线程,主线程结束其他线程仍然会继续执行

实现方式

  1. 继承 Thread 类,通过继承 Thread 类并重写 run() 方法来实现多线程

    • 简单易用,适合简单的多线程任务
    • 由于 Java 是单继承,继承 Thread 类会限制类的扩展性
    class MyThread extends Thread {
        @Override
        public void run() {
            System.out.println("Thread is running: " + Thread.currentThread().getName());
        }
    }
    
    public class ThreadExample {
        public static void main(String[] args) {
            MyThread thread1 = new MyThread();
            MyThread thread2 = new MyThread();
    
            thread1.start(); // 启动线程
            thread2.start();
        }
    }
    
  2. 实现 Runnable 接口,通过实现 Runnable 接口并实现 run() 方法来实现多线程

    • 更灵活,适合需要扩展其他类的情况

    • 推荐使用,因为 Java 支持多接口实现

    • 需要用Thread类包裹,因为只有Thread类中存在start()方法

      class MyRunnable implements Runnable {
          @Override
          public void run() {
              System.out.println("Thread is running: " + Thread.currentThread().getName());
          }
      }
      
      public class RunnableExample {
          public static void main(String[] args) {
              Thread thread1 = new Thread(new MyRunnable());
              Thread thread2 = new Thread(new MyRunnable());
      
              thread1.start(); // 启动线程
              thread2.start();
          }
      }
      
  3. 实现 Callable 接口

    • 通过实现 Callable 接口并实现 call() 方法来实现多线程。Callable 接口允许线程返回结果并抛出异常

    • 允许线程返回结果

    • 可以抛出异常,适合需要处理异常的场景。因为Callable 中的方法抛出了Exception异常而Runnable中的没有,所以Callable 的实现类可以抛出任意异常而Runnable子类只能抛出运行时异常

    • Future.get() 方法会阻塞当前线程,直到任务完成并返回结果

    • 可以通过 Future.isDone() 检查任务是否完成

    • 为了避免 Future.get() 无限期阻塞当前线程,可以设置超时时间。如果任务在指定时间内未完成,Future.get() 会抛出 TimeoutException

      import java.util.concurrent.Callable;
      import java.util.concurrent.FutureTask;
      
      class MyCallable implements Callable<String> {
          @Override
          public String call() throws Exception {
              return "Thread is running: " + Thread.currentThread().getName();
          }
      }
      /*
      FutureTask 是 Future 接口的一个实现类,同时实现了 Runnable 接口。它可以直接包装 Callable 任务,并通过 Thread 或线程池执行
      */
      public class CallableExample {
          public static void main(String[] args) throws Exception {
              FutureTask<String> futureTask = new FutureTask<>(new MyCallable());
              Thread thread = new Thread(futureTask);
      
              thread.start(); // 启动线程
              System.out.println(futureTask.get()); // 获取线程返回值
          }
      }
      

      第二种方式,通过线程池执行Callable接口对象行为,直接返回保存结果的Future对象

      import java.util.concurrent.*;
      
      public class CallableExample {
          public static void main(String[] args) throws Exception {
              // 创建线程池
              ExecutorService executor = Executors.newSingleThreadExecutor();
      
              // 提交 Callable 任务
              Future<String> future = executor.submit(new Callable<String>() {
                  @Override
                  public String call() throws Exception {
                      return "Task is completed!";
                  }
              });
      
              // 获取任务结果
              String result = future.get();
              System.out.println(result); // 输出: Task is completed!
      
              // 关闭线程池
              executor.shutdown();
          }
      }
      
  4. 使用线程池

    • 通过线程池管理多个线程,避免频繁创建和销毁线程的开销

    • 线程池中可以接收 Runnable 或 Callable 对象,因为Thread 实现了 Runnable 接口,所以executor.submit(thread) 可以使用,但这是 不推荐 的用法

      import java.util.concurrent.ExecutorService;
      import java.util.concurrent.Executors;
      
      public class ThreadPoolExample {
          public static void main(String[] args) {
              ExecutorService executor = Executors.newFixedThreadPool(2); // 创建线程池
      
              executor.submit(() -> {
                  System.out.println("Task 1 is running: " + Thread.currentThread().getName());
              });
      
              executor.submit(() -> {
                  System.out.println("Task 2 is running: " + Thread.currentThread().getName());
              });
      
              executor.shutdown(); // 关闭线程池
          }
      }
      

线程的生命周期

Java 中的线程生命周期包括以下7中,用Thread.State枚举表示了线程的几种状态

  • 新建(New)
  • 就绪(Runnable)
  • 运行(Running)
  • 阻塞(Blocked)
  • 等待(Waiting)
  • 超时等待(Timed Waiting)
  • 终止(Terminated)

可以通过 Thread.getState() 方法获取线程的当前状态

  1. 新建

    • 定义:线程对象被创建,但尚未启动
    • 触发条件:通过 new Thread() 创建线程对象
    • 状态转换:调用 start() 方法后,线程进入 就绪(Runnable) 状态
  2. 就绪

    • 定义:线程已经启动,等待 CPU 调度执行
    • 触发条件:调用 start() 方法后,线程进入就绪状态
    • 状态转换:
      • 当线程获得 CPU 时间片时,进入 运行(Running) 状态
      • 如果线程被阻塞或等待,进入 阻塞(Blocked) 或 等待(Waiting) 状态
  3. 运行

    • 定义:线程正在执行任务
    • 触发条件:线程获得 CPU 时间片
    • 状态转换
      • 当线程的时间片用完或主动让出 CPU 时,回到 就绪(Runnable) 状态
      • 如果线程被阻塞或等待,进入 阻塞(Blocked) 或 等待(Waiting) 状态
      • 当任务执行完毕时,进入 终止(Terminated) 状态
  4. 阻塞

    • 定义:线程因等待锁或其他资源而暂停执行
    • 触发条件
      • 线程尝试获取一个被其他线程持有的锁
      • 线程等待 I/O 操作完成
    • 状态转换
      • 当线程获得锁或资源时,回到 就绪(Runnable) 状态
  5. 等待

    • 定义:线程因等待其他线程的通知而暂停执行
    • 触发条件
      • 调用 Object.wait() 方法
      • 调用 Object.join() 方法(不带超时参数)
    • 状态转换
      • 当其他线程调用 notify() 或 notifyAll() 时,线程回到 就绪(Runnable) 状态
  6. 超时等待

    • 线程因等待其他线程的通知或超时而暂停执行

    • 触发条件

      • 调用 Thread.sleep(long millis) 方法

        • 让当前线程暂停执行指定的时间(毫秒)
        • 不释放锁:sleep() 方法不会释放当前线程持有的锁
        • 静态方法:sleep() 是 Thread 类的静态方法,作用于当前线程
        • 可中断:sleep() 方法可能会被中断,抛出 InterruptedException
      • 调用 Object.wait(long timeout) 方法

        • 让当前线程暂停执行,并释放对象的锁,直到以下条件之一发生
          • 其他线程调用该对象的 notify() 或 notifyAll() 方法
          • 指定的超时时间到达
        • 释放锁:wait() 方法会释放当前线程持有的对象锁
        • 必须在同步块中调用:wait() 方法必须在 synchronized 块或方法中调用,否则会抛出 IllegalMonitorStateException
        • 可中断:wait() 方法可能会被中断,抛出 InterruptedException
          • 如果 wait() 方法被中断,线程会从 wait() 方法返回,并尝试重新获取锁
          • 如果锁可用,线程会成功获取锁,并继续执行 wait() 之后的代码
          • 如果锁被其他线程持有,线程会进入 阻塞(Blocked) 状态,等待锁的释放
        public class WaitExample {
            private static final Object lock = new Object();
        
            public static void main(String[] args) throws InterruptedException {
                Thread thread = new Thread(() -> {
                    synchronized (lock) {
                        try {
                            System.out.println("Thread is waiting...");
                            lock.wait(2000); // 等待 2 秒或被唤醒
                            System.out.println("Thread is awake!");
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                });
        
                thread.start();
                Thread.sleep(1000); // 主线程等待 1 秒
                synchronized (lock) {
                    lock.notify(); // 唤醒等待的线程
                }
            }
        }
        
      • 调用 Object.join(long millis) 方法

    • 状态转换

      • 当等待时间到达或收到通知时,线程回到 就绪(Runnable) 状态
  7. 终止

    • 定义:线程执行完毕或被强制终止
    • 触发条件
      • 线程的 run() 方法执行完毕
      • 线程被强制终止(如调用 stop() 方法,但不推荐使用)
    • 状态转换
      • 线程终止后无法再回到其他状态
posted @ 2025-03-17 21:07  QAQ001  阅读(13)  评论(0)    收藏  举报