多线程知识点核心笔记

线程概念

任务: 一个“任务”常常通过实现某种接口( 如 Java 中的 RunnableCallable)来定义 。 线程是执行任务的“工人”,任务是“要完成的具体工作”。

线程 (Thread):

  • 一个进程内独立的执行单元。
  • 是CPU调度的最小单位。
  • 多个线程共享同一个进程的资源(如内存空间)。

进程 (Process):

  • 操作系统中运行的一个程序的实例。
  • 拥有独立的内存空间和系统资源。
  • 一个进程可以包含一个或多个线程。

并发: 指的是多个任务在同一个时间段内看似同时进行。 在单核CPU上实现并发是通过时间片轮转

并行: 指的是多个任务在同一个时刻真正地同时执行。 这需要多核CPU或多处理器系统,每个核心或处理器执行一个任务。

调度器: 操作系统或JVM的一部分,负责决定哪个线程在何时、哪个CPU核心上运行,以及线程状态之间的切换。

同步 : 用于协调多个线程对共享资源的访问,防止出现数据不一致或损坏。

竞态条件: 当多个线程尝试同时访问和修改共享数据时,由于执行顺序不确定,导致最终结果依赖于线程运行的竞速”情况,

上下文切换 : CPU从一个线程切换到另一个线程执行时,需要保存当前线程的运行状态(如寄存器值、程序计数器等),然后加载下一个线程的运行状态。频繁的上下文切换会带来性能开销。

守护线程:主要用于执行那些为用户线程提供后台服务支持性工作的任务。守护线程很像一个辅助线程,在后台不停的执行一些操作

普通方法调用 vs 多线程

Thread 类

上面讲了线程的一些术语和概念,线程就是在程序中执行任务的单元,多个线程就有多个执行任务的单元。相当于多个人干活,看看 java 怎么实现多线程。

Thread 类

Thread 单词含义 细线,【计】线程。

定义:

  • java.lang.Thread 类是 Java 中定义和控制线程的核心类
  • 它表示程序中的一个执行线程。一个程序可以同时运行多个线程。

作用:

  • 允许程序创建和管理自己的执行流程(线程)。
  • 是实现多线程的基础。

使用方式

继承 Thread 类并重写 run() 方法:

  • 创建一个新类,extends Thread
  • 在新类中 override public void run() 方法,将线程要执行的代码逻辑放入其中。
  • 创建该类的实例:MyThread thread = new MyThread();
  • 调用线程对象的 start() 方法来启动线程:thread.start();
    • start() 方法是启动一个新线程的关键,新线程会自动调用你重写的 run() 方法。
    • 注意:切勿直接调用 run() 方法 (thread.run()),那样只是在当前线程中像调用普通方法一样执行 run() 中的代码,不会创建新线程。
class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("线程运行:" + Thread.currentThread().getName());
    }
}

public class Main {
    public static void main(String[] args) {
        MyThread t = new MyThread();
        t.start(); // 启动线程
    }
}

Runnable 接口

定义:

  • java.lang.Runnable 是一个函数式接口,定义在 java.lang 包中。
  • 它表示一个可运行的任务

作用:

  • 用来封装需要在单独线程中执行的业务逻辑代码。
  • 它将任务的定义线程的控制(如启动、调度)分离开来。

核心方法:

  • public abstract void run();
    • 这是一个无参数、无返回值的方法。
    • 这个方法体中包含了需要在线程中执行的具体任务代码。
    • 注意:此方法不能直接声明抛出受查异常(Checked Exception),如果任务代码可能抛出受查异常,需要在 run() 方法内部捕获并处理,或者转换为运行时异常抛出。

如何使用 Runnable 创建和运行线程:

这是创建线程的推荐方式之一(相比于继承 Thread 类)。

  1. 创建一个类,实现 Runnable 接口: Java
class MyTask implements Runnable {
    @Override
    public void run() {
        // 线程要执行的任务代码
        System.out.println("Task is running in thread: " + Thread.currentThread().getName());
    }

    
}
  1. 创建实现 Runnable 接口的类的对象(这就是你的任务实例): Java
main{
    MyTask task = new MyTask(); 
}
  1. 创建一个 java.lang.Thread 对象,并将 Runnable 任务实例作为构造方法的参数传入: Java
main{
    MyTask task = new MyTask(); 
    Thread thread = new Thread(task);
// 还可以指定线程名称:
// Thread thread = new Thread(task, "MyWorkerThread");
}
  1. 调用 Thread 对象的 start() 方法来启动线程: Java
main{
    MyTask task = new MyTask(); 
    Thread thread = new Thread(task);
// 还可以指定线程名称:
// Thread thread = new Thread(task, "MyWorkerThread");
    thread.start(); // 启动新线程,并在这个新线程中执行 task 对象的 run() 方法
}

Runnable 比 Thread 更灵活

Thread 的单继承限制 :

  • Java 类只能继承一个父类。如果你通过继承 Thread 类来定义线程任务,那么你的类就不能再继承其他任何类了。这在你的类需要同时拥有某个父类的特性(比如一个 Swing 组件类 MyPanel extends JPanel)又想作为线程任务执行时,就会遇到冲突。
  • 而实现 Runnable 接口则没有这个限制。你的类可以继承其他类,同时实现 Runnable 接口,从而既拥有父类的功能,又具备作为线程任务执行的能力(class MyPanel extends JPanel implements Runnable { ... }。这是 Runnable 最直接和重要的灵活之处。

任务逻辑与线程对象的解耦 :

  • 继承 Thread 类时,你的类就是一个线程,任务逻辑 (run 方法) 是这个线程对象的一部分。 (子类继承Thread 类,重写Run方法,那你的子类不就是一个线程类)
  • 实现 Runnable 接口时,你的类定义的是一个任务 (Task),类时任务,能给别的任意 Thread 类执行,任务和线程就解耦了!

更好的代码结构和复用性 :

  • 由于任务逻辑是独立的 Runnable 对象,你可以创建多个 Thread 对象来运行同一个 Runnable 实例(如果任务是线程安全的),或者多个 Thread 对象运行同一个 Runnable 类创建的不同实例。 Runnable 对象可复用。

集成线程池:

ExecutorService 的设计就是直接接收 RunnableCallable 类型的任务。你只需要将你的任务逻辑封装到 Runnable 对象中,就可以直接提交给线程池执行。

并发问题

多个线程同时操作同一个资源,导致数据不一致。

多线程+for 要注意的问题

看代码:

    @Override
    public void run() {
   
        for (int i = 0; i <= 100; i++) {
            if(Thread.currentThread().getName().equals(rabbit) && i % 2 == 0){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println(Thread.currentThread().getName() + "走了" + i + "步");
            boolean flag = gameOver(i);
            if (flag){
                break;
            }
        }
    }

  public static void main(String[] args) {
  
        new Thread(race,tortoise).start();
        new Thread(race,rabbit).start();
    }

两个线程,一个叫 tortoise, 一个叫 rabbit, tortoise 先过了重点,然后 break for 循环,此时 break 掉的也只是 tortoise线程的循环,rabbit 畅通无阻,继续运行。所以需要加一个条件,把 rabbit 也 break 了。就是把下面注释的 if 解开

    public boolean gameOver(int steps) {
        // if(winner != null){
        //     return true;
        // }
        if (steps >= 3){
            winner = Thread.currentThread().getName();
            System.out.println(winner + "到达终点");
            return true;
        }
        return false;
    }

断点调试问题

断点的默认行为是“暂停所有线程", 调试器会把整个 JVM 的所有线程都挂起。

如果你想暂停某一个线程,那么右键红色断点,如下图设置

Thread.currentThread().getName().equals(Race.tortoise)

一些对线程的操作

线程状态

new Thread -> Thread.start() -> 等待 cpu 调度 -> 运行状态 -> Sleep 阻塞状态 ->线程结束,kill

基本操作

停止线程

  • 官方说不建议用 stop
  • 建议使用一个条件,让线程正常结束停止,或者程序自然停止

线程休眠:

sleep 存在异常 InterruptedException , 需要try-catch

sleep 时间到达后就进入就绪状态,等待 cpu 调度

sleep 可以模拟网络延时,倒计时等

sleep 不会释放锁

线程礼让(Yield):

礼让线程 : 让当前正在执行的线程暂停,但不阻塞。将线程转为就绪状态。

让 cpu 重新调度,不过不一定礼让成功。就是回到就绪状态,重新竞速。

实现:Thread.Yield() 简单啊!

线程强制执行:

使用Thread.join 强制执行此线程

示例:

public class MinimalJoinExample {

    public static void main(String[] args) {
        System.out.println("主线程开始."); // 1. 主线程开始

        // 创建并启动一个子线程
        Thread childThread = new Thread(() -> {
            System.out.println("子线程开始."); // 2. 子线程开始
            try {
                // 模拟子线程执行任务(例如,耗时操作)
                Thread.sleep(2000); // 子线程休眠 2 秒
            } catch (InterruptedException e) {
                System.out.println("子线程被中断.");
            }
            System.out.println("子线程结束."); // 3. 子线程结束
        });

        childThread.start(); // 启动子线程

        System.out.println("主线程等待子线程完成..."); // 4. 主线程在等待

        try {
            // *** 核心代码:主线程在这里调用 join() 等待子线程 ***
            childThread.join(); // 主线程会在这里暂停,直到 childThread 执行完毕
        } catch (InterruptedException e) {
            System.out.println("主线程在等待时被中断.");
        }

        System.out.println("主线程继续执行."); // 5. 子线程结束后,主线程恢复
        System.out.println("主线程结束."); // 6. 主线程最终结束
    }
}

观察线程状态(State):

Thead.State 这是一个枚举类型

Thread.State state = thread.getState() 获取到线程状态

线程优先级(Priority):

1-10个等级,优先级越高,权重越大,cpu 越可能调用。

主线程优先级不可更改。

实现:

设置了优先级再启动,反之没用。

Thread.setPriority(1);

Thread.start();

守护线程(Deamon):

主要用于执行那些为用户线程提供后台服务支持性工作的任务。

守护线程很像一个辅助线程,在后台不停的执行一些操作

实现:

Thread.setDeamon(true); //默认是 false

线程同步

术语:

阻塞 (Blocking): 当一个线程尝试获取一个锁,但锁当前被其他线程持有,该线程就会暂停执行,进入等待状态,直到锁被释放

可中断 (Interruptible): 指的是线程在执行某个阻塞操作(比如 Thread.sleep(), Object.wait(), 或者这里的 lockInterruptibly())时,如果其他线程调用了这个阻塞线程的 interrupt() 方法,该阻塞操作会立即停止等待,并抛出一个 InterruptedException,同时清除线程的中断标志。

可重入性: 可重入性确实是它们的一个基本且非常重要的特性。如果一个线程已经成功获取了某个锁,那么当这个线程再次尝试获取由同一个锁保护的其他(或同一个)资源时,它不会被阻塞,能够直接再次进入。

Synchronized :一个关键字写在方法或代码块上

Lock

Lock :JUC包下它提供了一个更灵活、更广泛的锁定操作,作为 synchronized 关键字的替代或补充

Lock 是一个接口, 在 Java 标准库 java.util.concurrent.locks 包中 ,主要的实现类 ReentrantLockReentrantReadWriteLock

ReentrantLock :是 Lock 常用的实现类,可以通过构造方法实现公平锁,公平锁会尽量保证线程获取锁的顺序是按照它们请求锁的顺序来,等待时间越长越优先。

Lock 接口的方法

表示了锁的多样性

  • lock(): 与 synchronized 类似,阻塞直到获取锁(不可响应中断)。

  • tryLock(): 非阻塞尝试。如果能立即获取锁,返回 true;否则,立即返回 false

  • tryLock(long time, TimeUnit unit): 定时尝试。在指定的时间内尝试获取锁,如果在指定时间内未能获取到锁(即超时),方法返回 false表示获取锁失败。如果在等待过程中线程被中断,使用 interrupt() ,方法会抛出 InterruptedException 异常,不会返回 truefalse,也不会获取锁。

  • lockInterruptibly(): 可中断阻塞。如果锁被占用,当前线程阻塞等待;但如果等待过程中线程被中断,会抛出 InterruptedException 并停止等待。 关键点: 当线程正在阻塞等待这个锁的过程中,如果有其他线程调用了该阻塞线程的 interrupt() 方法来中断它,那么 lockInterruptibly() 方法会立即停止等待,放弃获取锁,并向当前线程抛出一个 InterruptedException

  • Lock 通过 newCondition() 方法可以创建多个Condition 对象与同一个 Lock 关联。每个 Condition 对象都有自己的等待队列,队列存线程, 线程在调用 condition1.await() 时,只会进入 condition1 的等待队列 。线程可以在特定的 Condition 上等待 (Condition.await()),并只通过对该 Condition 调用 signal()signalAll() 来唤醒等待在这个特定条件上的线程。

Lock 提供了更强的灵活性,但代价是需要手动管理锁的获取和释放,特别是必须将 unlock() 放在 finally 块中,否则容易造成死锁或资源泄露

为什么要用 lockInterruptibly()

Synchronized 是个简单好用不灵活的家伙,不能中断,傻傻的等

在某些场景下,你可能不希望一个线程无限期地等待一个锁。例如,在一个 GUI 应用中,用户可能点击了一个“取消”按钮,你希望一个正在后台等待资源的线程能够响应这个“取消”信号并终止等待。如果使用 lock(),你很难优雅地让这个等待锁的线程停止;但如果使用 lockInterruptibly(),你就可以通过中断它来实现取消操作。

实现

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockInterruptiblyExample {

    // 共享的锁对象
    private static final Lock sharedLock = new ReentrantLock();

    // --- 任务 1: 持有锁一段时间的线程 ---
    static class LockHolderTask implements Runnable {
        @Override
        public void run() {
            sharedLock.lock(); // <-- 使用 lock() 方法获取锁 (不可中断阻塞)
            System.out.println(Thread.currentThread().getName() + " 已获取锁并正在持有.");
            try {
                // 模拟长时间持有锁
                System.out.println(Thread.currentThread().getName() + " 持有锁中,休眠 5 秒...");
                Thread.sleep(5000); // 休眠期间是可中断的,但这里我们关注的是锁持有本身
            } catch (InterruptedException e) {
                System.out.println(Thread.currentThread().getName() + " 在持有锁期间被中断.");
                // 处理中断,例如重新设置中断标志
                Thread.currentThread().interrupt();
            } finally {
                // 释放锁
                sharedLock.unlock();
                System.out.println(Thread.currentThread().getName() + " 释放了锁.");
            }
        }
    }

    // --- 任务 2: 尝试以可中断方式获取锁的线程 ---
    static class InterruptibleLockTask implements Runnable {
        @Override
       public void run() {
            System.out.println(Thread.currentThread().getName() + " 尝试以可中断方式获取锁...");
            try {
                // *** 核心代码:尝试以可中断方式获取锁 ***
                sharedLock.lockInterruptibly(); // 如果锁被占用,会在这里阻塞,并且如果被中断,会抛异常

                // 如果代码执行到这里,说明线程成功获取了锁
                // 在我们演示被中断的例子中,通常不会走到这里
                System.out.println(Thread.currentThread().getName() + " 成功获取锁 (如果被中断了则不应看到此消息).");
                try {
                     // 正常情况下在这里执行需要锁保护的代码...
                     System.out.println(Thread.currentThread().getName() + " 执行临界区代码...");
                     Thread.sleep(2000); // 模拟工作
                } finally {
                     sharedLock.unlock(); // 记得释放锁!
                     System.out.println(Thread.currentThread().getName() + " 释放了锁.");
                }


            } catch (InterruptedException e) {
                // *** 重点:在这里捕获中断异常 ***
                // 这意味着线程在 lockInterruptibly() 处阻塞等待锁时被中断了
                System.out.println(Thread.currentThread().getName() + " 在等待锁时被中断! 它**没有获取到锁**.");
                // 因为没有获取到锁,所以不需要调用 unlock()
            } finally {
                 // 这里可以做一些无论是否获取到锁都需要进行的清理工作
                 // System.out.println(Thread.currentThread().getName() + " 完成 try/catch 块的执行.");
            }
        }
    }


    public static void main(String[] args) throws InterruptedException {
        System.out.println("主线程启动.");

        // 1. 启动锁持有者线程,让它先获取并持有锁
        Thread lockHolderThread = new Thread(new LockHolderTask(), "锁持有者线程");
        lockHolderThread.start();

        // 给予锁持有者线程足够的时间来获取锁 (例如,休眠 1 秒)
        Thread.sleep(1000);

        // 2. 启动可中断等待者线程,它会尝试获取锁并被阻塞
        Thread interruptibleWaiterThread = new Thread(new InterruptibleLockTask(), "可中断等待者线程");
        interruptibleWaiterThread.start();

         // 给予可中断等待者线程足够的时间到达 lockInterruptibly() 并进入阻塞状态 (例如,再休眠 1 秒)
        Thread.sleep(1000);

        // 3. *** 从主线程中断正在等待锁的可中断等待者线程 ***
        System.out.println("主线程中断 [" + interruptibleWaiterThread.getName() + "]...");
        interruptibleWaiterThread.interrupt(); // 调用 interrupt()

        // 等待一段时间,让所有线程有机会执行或结束
        Thread.sleep(5000); // 等待,让锁持有者线程完成并释放锁

        System.out.println("主线程结束.");
    }
}

线程池

核心接口类

  • Executor 最基础的接口,只定义了一个方法 void execute(Runnable command),用于提交一个 Runnable 任务。
  • ExecutorService 继承自 Executor,是线程池的核心接口。它添加了更全面的功能:
    • 生命周期管理:shutdown(), shutdownNow(), isShutdown(), isTerminated(), awaitTermination()
    • 任务提交: 除了 execute(),还提供 submit() 方法,可以提交 RunnableCallable 任务,并返回代表任务执行结果的 Future 对象。
    • 批量任务提交:invokeAll(), invokeAny()
  • ThreadPoolExecutor 是线程池的真正核心 ,实现了 ExecutorExecutorService提供了丰富的配置参数:
    • corePoolSize:核心线程数。线程池维护的最少线程数量,即使空闲也不会被终止(除非设置 allowCoreThreadTimeOut)。
    • maximumPoolSize:最大线程数。线程池允许的最大线程数量。
    • keepAliveTime:非核心线程的空闲存活时间。当池中线程数大于核心线程数时,空闲时间超过此值的线程会被终止。
    • unitkeepAliveTime 的时间单位。
    • workQueue:任务队列。用于存放等待执行的任务。常见的队列有 ArrayBlockingQueue, LinkedBlockingQueue, SynchronousQueue, PriorityBlockingQueue 等。不同的队列对线程池的行为影响很大。
    • threadFactory:线程工厂。用于创建新线程,可以自定义线程的命名、优先级等。
    • handler:拒绝策略 (RejectedExecutionHandler)。当任务队列已满且线程池中的线程数达到最大线程数时,新提交的任务会触发拒绝策略。默认有几种拒绝策略(如抛异常、丢弃任务、丢弃队列最老的任务、使用提交任务的线程执行)。
创建线程池示例

创建固定大小线程池 (newFixedThreadPool)

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

public class FixedThreadPoolExample {
    public static void main(String[] args) {
        System.out.println("创建固定大小线程池...");

        // 创建一个拥有 3 个线程的固定大小线程池
        ExecutorService executor = Executors.newFixedThreadPool(3);

        // 提交 10 个任务给线程池
        for (int i = 0; i < 10; i++) {
            final int taskIndex = i;
            executor.execute(() -> {
                System.out.println(Thread.currentThread().getName() + " 正在执行任务 " + taskIndex);
                try {
                    Thread.sleep(1000); // 模拟任务执行时间
                } catch (InterruptedException e) {
                    System.out.println(Thread.currentThread().getName() + " 任务 " + taskIndex + " 被中断.");
                    Thread.currentThread().interrupt();
                }
                 System.out.println(Thread.currentThread().getName() + " 完成任务 " + taskIndex);
            });
        }

        // 关闭线程池 (不再接受新任务,但会执行已提交的任务)
        System.out.println("提交完所有任务,关闭线程池...");
        executor.shutdown();

        // 可选:等待线程池终止 (例如最多等 5 秒)
        try {
            if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
                System.err.println("线程池未在规定时间内终止,可能还有任务未完成。");
                // 如果需要在超时后强制停止,可以使用 executor.shutdownNow();
            }
        } catch (InterruptedException e) {
            System.err.println("等待线程池终止时被中断。");
            Thread.currentThread().interrupt();
        }

        System.out.println("主线程结束.");
    }
}

创建可缓存线程池 (newCachedThreadPool)

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

public class CachedThreadPoolExample {
    public static void main(String[] args) {
        System.out.println("创建可缓存线程池...");

        // 创建一个可缓存线程池
        ExecutorService executor = Executors.newCachedThreadPool();

         // 提交一些任务
        for (int i = 0; i < 5; i++) { // 提交 5 个任务
             final int taskIndex = i;
             executor.execute(() -> {
                System.out.println(Thread.currentThread().getName() + " 正在执行任务 " + taskIndex);
                try {
                    Thread.sleep(500); // 模拟任务执行时间
                } catch (InterruptedException e) {
                     System.out.println(Thread.currentThread().getName() + " 任务 " + taskIndex + " 被中断.");
                    Thread.currentThread().interrupt();
                }
                 System.out.println(Thread.currentThread().getName() + " 完成任务 " + taskIndex);
            });
        }

        // 注意:CachedThreadPool 的线程在空闲一定时间后会终止
        // 如果在提交任务后立即关闭,可能看不到所有线程被创建和回收的过程
        System.out.println("提交完所有任务,关闭线程池...");
        executor.shutdown();

        // 可选:等待线程池终止
         try {
            if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
                System.err.println("线程池未在规定时间内终止。");
            }
        } catch (InterruptedException e) {
            System.err.println("等待线程池终止时被中断。");
            Thread.currentThread().interrupt();
        }

        System.out.println("主线程结束.");
    }
}

使用 ThreadPoolExecutor 构造函数 (更灵活,推荐用于生产环境)

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.Executors; // 用于获取默认 ThreadFactory

public class ThreadPoolExecutorExample {
    public static void main(String[] args) {
        System.out.println("直接创建 ThreadPoolExecutor...");

        // 核心线程数:即使空闲也保留的线程数
        int corePoolSize = 2;
        // 最大线程数:池中最多允许的线程数
        int maximumPoolSize = 5;
        // 非核心线程的空闲存活时间:超过核心线程数且空闲超过此时间,线程会被回收
        long keepAliveTime = 60;
        // 存活时间单位
        TimeUnit unit = TimeUnit.SECONDS;
        // 任务队列:用于存放等待执行的任务。这里使用容量为 10 的有界阻塞队列。
        BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(10);
        // 线程工厂:用于创建新线程
        ThreadFactory threadFactory = Executors.defaultThreadFactory(); // 使用默认的线程工厂
        // 拒绝策略:当队列满且线程数达到最大时,如何处理新提交的任务。这里使用默认策略 (抛异常)。
        // RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy(); // 默认策略

        // 创建 ThreadPoolExecutor 实例
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
            corePoolSize,
            maximumPoolSize,
            keepAliveTime,
            unit,
            workQueue,
            threadFactory
            // , handler // 可以选择指定拒绝策略,不指定则使用默认的 AbortPolicy
        );

        // 提交任务
        for (int i = 0; i < 15; i++) { // 提交 15 个任务,队列容量 10 + 最大线程 5 = 15,刚好可能不触发拒绝策略
            final int taskIndex = i;
            try {
                 executor.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + " 正在执行任务 " + taskIndex);
                    try {
                        Thread.sleep(500); // 模拟任务执行时间
                    } catch (InterruptedException e) {
                         System.out.println(Thread.currentThread().getName() + " 任务 " + taskIndex + " 被中断.");
                        Thread.currentThread().interrupt();
                    }
                     System.out.println(Thread.currentThread().getName() + " 完成任务 " + taskIndex);
                });
            } catch (java.util.concurrent.RejectedExecutionException e) {
                 System.err.println("任务 " + taskIndex + " 被拒绝: " + e.getMessage());
            }
        }

        // 关闭线程池
        System.out.println("提交完所有任务,关闭线程池...");
        executor.shutdown();

        // 可选:等待线程池终止
         try {
            if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
                System.err.println("线程池未在规定时间内终止。");
            }
        } catch (InterruptedException e) {
            System.err.println("等待线程池终止时被中断。");
            Thread.currentThread().interrupt();
        }

        System.out.println("主线程结束.");
    }
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值