一. 线程创建的四种方式?

通过实现Thread类和实现RUNNABLE和Callable 接口 + FutureTask

高并发场景:使用线程池

Callable接口使用:

1. 用线程池 + submit + Callable + Future

2. Thread + Callable + FutureTask

RUNNABLE接口的使用:

Thread+Runnable

二. future和futuretask的区别?

future是一个接口,只用来获取异步的结果。futureTask是一个类,它实现了runnable和future接口。既可以作为任务给线程池进行处理,也可以通过get方法来获取线程执行结果的返回值。

三. yeild,join,wait,sleep区别?

方法 所属类 是否释放锁 用途 使用场景
yield() Thread ❌ 不释放锁 暂时让出 CPU,让其他同优先级线程有机会执行,不阻塞 提高线程调度灵活性(较少使用)
join() Thread ❌ 不释放锁 当前线程等待另一个线程执行完毕 线程串行执行(A 等 B 执行完)
wait() Object ✅ 释放锁 当前线程等待,被唤醒后重新竞争锁(需配合 notify/notifyAll 多线程协作(生产者-消费者模型)
sleep() Thread ❌ 不释放锁 当前线程休眠指定时间,到时继续执行 定时、模拟延迟、轮询场景等

四. 线程的几种状态?

   新建,就绪,执行,阻塞,终止。

New(新建) 调用 new Thread() 创建线程,但未启动
Runnable(就绪) 调用 start() 后进入就绪队列,等待 CPU 调度
Running(执行) 获得 CPU 时间片,开始执行 run() 方法
Blocked / Waiting(阻塞/等待) 执行中被挂起,如 sleep()wait()、等待 IO
Terminated(终止) 线程执行完毕或抛出异常,生命周期结束

五. 线程如何进行通信?

三种方式:

1.  volatile :通过定义一个全局变量

public class VolatileDemo {
    private static volatile boolean flag = true;

    public static void main(String[] args) {
        new Thread(() -> {
            System.out.println("线程A 等待 flag 变为 false...");
            while (flag) {} // 轮询等待
            System.out.println("线程A 结束");
        }).start();

        new Thread(() -> {
            try { Thread.sleep(1000); } catch (InterruptedException e) {}
            flag = false;
            System.out.println("线程B 修改了 flag 为 false");
        }).start();
    }
}

    2.  synchronized和wait/notify/notify all  实现线程的通信 

         1. 无法精准的唤醒   2. 不能中断   3. 只能等待一个队列

         wait/notify/notify all必须放在同步代码块或者同步方法中。

         理由:这几个方法基于对象的监视器锁实现的,只有持有对象的监视器的锁,才能调用这些方法。

public class WaitNotifyExample {
    private static final Object lock = new Object();
    private static int number = 1;
    private static final int MAX = 10;

    public static void main(String[] args) {
        Thread oddThread = new Thread(() -> {
            while (number <= MAX) {
                synchronized (lock) {
                    if (number % 2 == 1) {
                        System.out.println("奇数线程:" + number++);
                        lock.notify(); // 唤醒偶数线程
                    } else {
                        try {
                            lock.wait(); // 等待偶数线程
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        });

        Thread evenThread = new Thread(() -> {
            while (number <= MAX) {
                synchronized (lock) {
                    if (number % 2 == 0) {
                        System.out.println("偶数线程:" + number++);
                        lock.notify(); // 唤醒奇数线程
                    } else {
                        try {
                            lock.wait(); // 等待奇数线程
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        });

        oddThread.start();
        evenThread.start();
    }
}

    3,ReentrantLock和Condition(await, signal, signal all)

         1. 精准唤醒      2. 支持中断     3. 多条件等待   4. 支持公平锁

     使用场景: 实际开发中,如果只需要简单互斥和通知,用 synchronized 即可;但如果有多个条         件、需要可中断或更灵活的锁控制,就推荐用 ReentrantLockCondition。   

    案例1:一个lock和三个condition条件等待

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

public class ABCPrinter {
    private final ReentrantLock lock = new ReentrantLock();
    private final Condition condA = lock.newCondition();
    private final Condition condB = lock.newCondition();
    private final Condition condC = lock.newCondition();

    private int state = 0; // 0: A, 1: B, 2: C

    public void printA() {
        lock.lock();
        try {
            while (state % 3 != 0) {
                condA.await();
            }
            System.out.println("A");
            state++;
            condB.signal(); // 唤醒打印B的线程
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            lock.unlock();
        }
    }

    public void printB() {
        lock.lock();
        try {
            while (state % 3 != 1) {
                condB.await();
            }
            System.out.println("B");
            state++;
            condC.signal(); // 唤醒打印C的线程
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            lock.unlock();
        }
    }

    public void printC() {
        lock.lock();
        try {
            while (state % 3 != 2) {
                condC.await();
            }
            System.out.println("C");
            state++;
            condA.signal(); // 唤醒打印A的线程
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        ABCPrinter printer = new ABCPrinter();

        Runnable a = () -> {
            for (int i = 0; i < 5; i++) {
                printer.printA();
            }
        };

        Runnable b = () -> {
            for (int i = 0; i < 5; i++) {
                printer.printB();
            }
        };

        Runnable c = () -> {
            for (int i = 0; i < 5; i++) {
                printer.printC();
            }
        };

        new Thread(a).start();
        new Thread(b).start();
        new Thread(c).start();
    }
}

  • 使用 ReentrantLock 进行互斥控制,避免多个线程并发修改 state

  • 每个线程使用不同的Condition,实现精准唤醒

  • 每次执行都判断 state % 3,确保按顺序执行

  • 每轮只唤醒下一个目标线程,提升效率,避免 notifyAll 的资源竞争问题

六. 线程池的七大核心参数?

总的来说有七大核心参数。

1. 核心线程数   2. 最大线程数  3. 任务队列  4. 线程的工厂   5. 线程存活的时间  6. 线程存活时间的单位  7. 拒绝策略

类型 corePoolSize maximumPoolSize
CPU 密集型 CPU核心数 CPU核心数 + 1
IO 密集型 CPU核心数 2 × CPU核心数(或更多)

int cpuCount = Runtime.getRuntime().availableProcessors();
ThreadPoolExecutor executor = new ThreadPoolExecutor(
    cpuCount,                        // corePoolSize             //cpu的核数
    cpuCount * 2,                 // maximumPoolSize    //根据cpu和io密集型判断
    60L, TimeUnit.SECONDS,        // keepAliveTime + unit    
    new ArrayBlockingQueue<>(100),// 有界队列防止 OOM    
    Executors.defaultThreadFactory(),
    new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);

线程池哪几种队列:ArrayBlockingQueue有界队列    LinkedBlockingQueue  无界队列

PriorityBlockingQueue      按优先级执行任务,需要任务实现 Comparable

七. 线程池工作的流程?

总的来说,线程池是通过(核心线程数+任务队列+最大线程数+拒绝策略)这套机制,实现任务的 弹性处理和资源高效的利用。避免了线程的频繁创建造成资源的浪费。

具体工作流程:往线程池提交任务的时候,先创建核心线程,随着提交任务的增加,如果核心线程满了,会将提交的任务加到任务队列里面。如果任务队列满了,会创先最大线程来执行任务。如果最大线程数满了,会执行对应的拒绝策略。

当任务执行完以后,非核心线程达到keepalivetime时间以后会被销毁。核心线程可以长期的存活。

八. 线程池的拒绝策略?

默认四种拒绝策略。

1. 直接抛出异常:不允许丢弃任务(默认的拒绝策略)

2. 丢弃任务不抛出异常:运行任务丢失

3. 让提交任务的线程来执行该任务

4. 丢弃掉队列中最早的任务,然后尝试执行新的任务

九. 线程池几种阻塞队列?

一共5种。 有界队列(数组实现),无界队列(链表实现),延迟队列,优先级队列,同步队列。

线程池提交 execute 和 submit 有什么区别?

execute 没有返回值。适应场景:不关心异常和返回值。

submit 有返回值。适应场景:关心异常和返回值。

线程池关闭的两种方式?

shutdown():平滑关闭。任务执行完毕以后关闭。

行为

  • 停止接收新任务。

  • 已提交的任务(包括排队中和正在执行的)会继续执行完成

  • 等所有任务执行完后,线程池才会真正关闭。

shutdownNow():立即关闭。

行为

  • 停止接收新任务。

  • 尝试中断正在执行的任务(通过 Thread.interrupt() 实现)。

  • 返回还未开始执行的任务列表(List<Runnable>)。

需要注意的是,shutdownNow 不会真正终止正在运行的任务,只是给任务线程发送 interrupt 信号,任务是否能真正终止取决于线程是否响应 InterruptedException。

线程池的核心参数的设置?

CPU密集型:涉及到大量的CPU的计算,为了避免大量的上下文切换,线程数设置为

CPU核心数+1。

IO密集型:涉及到大量的IO操作(网络,数据库数据读取),会出现阻塞的情况,线程设置都多一些,设置为CPU核心数的2倍。

十. 常见的线程池有哪些 ?

线程池自带的四种线程池。

1. 固定大小的线程池(fixedThreadPool):适合固定任务数的场景

2. 带缓存的线程池(shceduleThreadPool)

3. 定时任务线程池

4. 单一的线程池(singleThreadPool)

为啥使用手动创建而不使用自带的线程池?

总: Executors自带的几种线程池,虽然使用简单,但是自带的参数存在一些风险。因此,采用手动创建线程池,来灵活控制线程池的参数。

分: 比如:newfixedThreadPool内部的队列是链表实现的无界队列,可以缓存大量的任务,可能造成OOM现象。newcacheThreadPool在高并发场景下面,可以创建Integer.Max个线程,容易出现OOM现象。

 十一. 线程池使用过程中应该注意什么?

1. 合适的线程池的大小

2. 合适的任务队列

3.手动创建线程池

十二. 线程池执行中断电了应该怎么处理?

1. 持久化操作:持久化任务。可以将任务持久化到数据库或者消息队列中,等电恢复后再重新执行。

2. 幂等性操作: 任务幂等性,需要保证任务是幂等的,也就是无论执行多少次,结果都一致。

十二. 线程池怎么捕获异常

1. try  catch捕获异常      2. future  的get方法获取异常


十二.  面试线程池带有返回值如何处理

public class FtpUploadManager {

    // 模拟从本地服务器读取文件并上传到远端服务器
    public static String readAndUpload(String serverIp) {
      try {
            System.out.println("开始处理服务器: " + serverIp + ",线程:" + Thread.currentThread().getName());
            // 模拟读取文件(1秒)
            Thread.sleep(1000);
            System.out.println("读取文件成功,服务器: " + serverIp);

            // 模拟上传文件(1秒)
            Thread.sleep(1000);
            System.out.println("上传文件成功,服务器: " + serverIp);
            return "成功:" + serverIp;

        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return "失败:" + serverIp;
        }
    }

    public static void main(String[] args) {
        // 模拟五台服务器的IP
        List<String> serverIps = List.of(
                "192.168.1.1",
                "192.168.1.2",
                "192.168.1.3",
                "192.168.1.4",
                "192.168.1.5"
        );

        // 自定义线程池(核心线程5,最大线程10)
        ExecutorService executor = new ThreadPoolExecutor(
                5, 10,
                60, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(10),
                new ThreadFactory() {
                    private final AtomicInteger count = new AtomicInteger(1);
                    public Thread newThread(Runnable r) {
                        return new Thread(r, "ftp-thread-" + count.getAndIncrement());
                    }
                },
                new ThreadPoolExecutor.AbortPolicy()
        );

        // 存放每个任务的Future
        List<CompletableFuture<String>> futureList = new ArrayList<>();

        for (String ip : serverIps) {
            // 每个任务封装成CompletableFuture异步提交
            CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> readAndUpload(ip), executor);
            futureList.add(future);
        }  在for循环内部通过completetableFuture.supplyAsync()创建线程池异步任务

        // 等待所有任务完成
        CompletableFuture<Void> all = CompletableFuture.allOf(futureList.toArray(new CompletableFuture[0]));

        try {
            // 超时时间:最多等待6秒
            all.get(6, TimeUnit.SECONDS);

            // 获取每个任务的结果(非阻塞)
            for (CompletableFuture<String> future : futureList) {
                System.out.println("执行结果:" + future.getNow());
            }

        } catch (TimeoutException e) {
            System.out.println("任务超时,未全部完成!");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            executor.shutdown();
        }
    }
}

  supplyAsyc(): 创建异步任务

  CompletableFuture.allOf等待所有任务执行完成

 get(timeout, unit)设置超时时间

实现步骤:主要通过ThreadPoolExecutor,CompletableFuture两个类实现的。

1. 通过new ThreadPoolExecutor()方式创建线程池

2. CompletableFuture.supplyasyc()创建异步任务

3. 并通过一个futurelist来存储每个异步任务的返回结果(future)

4. CompletableFuture.allof()方法保证所有的线程都执行完成

5. CompletableFuture.allof()方法返回的对象(新生成CompletableFuture对象)设置超时时间

6. get(timeout,unit)

7. 最后通过遍历futurelist对象获取每个future中的结果

8. 最后将结果拼凑返回给对方厂家
==================================================================================

十三. 线程池常用的队列

一. LinkedBlockingDeque<T> queue = new LinkedBlockingDeque<>();

1. 双端阻塞队列

2. 线程安全,内部使用reentrantLock实现,多个线程可以并发访问。

阻塞特性:

1. 当队列为空时,take()方法会阻塞直到有元素可有。

2. 当队列为满的时候,put()方法会阻塞直到有空间可用。

可以用于生产者和消费者模型。

生产者时时的生产数据存入任务队列(比如解析大的文件),消费者消费数据。

(为了避免内存爆满,每次大于500条即入库,入库以后清空原来的List)

我不能保证写的每个地方都是对的,但是至少能保证不复制、不黏贴,保证每一句话、每一行代码都经过了认真的推敲、仔细的斟酌。每一篇文章的背后,希望都能看到自己对于技术、对于生活的态度。


我相信乔布斯说的,只有那些疯狂到认为自己可以改变世界的人才能真正地改变世界。面对压力,我可以挑灯夜战、不眠不休;面对困难,我愿意迎难而上、永不退缩。


其实我想说的是,我只是一个程序员,这就是我现在纯粹人生的全部。
==================================================================================

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐