面试中的线程题

原文链接:线程题大全

Java 并发库同步辅助类

CountDownLatch

工作机制:初始化一个计数器,此计数器的值表示需要等待的事件数量。

提供了两个主要方法:

  • await():当一个线程调用此方法时,它将阻塞,直到计数器的值为 0
  • countDown():用于减少计数器的值。通常表示一个事件已经发生了(如任务完成),当计数器的值减到 0 时,所有调用 await()并阻塞的线程将被唤醒并继续执行

重要特性:

  • 不可重置:一旦计数器的值为 0,就不能再被重置回初识值或其他任何值
  • 一次性的:计数值到达 0 后,所有在 await()方法上等待的线程将被释放,而后续的 await()方法调用将立即通过,不会进行阻塞
  • 多用途同步工具:能被用于多种目的,等待服务的初始化、一组任务或某个事件的发生

示例:在两个工作线程结束后再调用主线程

java 复制代码
CountDownLatch latch = new CountDownLatch(2); // 设定计数器初始值为2

// 创建第一个线程,完成某项任务后调用countDown方法
new Thread(() -> {
    System.out.println("线程1执行...");
    latch.countDown();
    System.out.println("线程1完成操作,计数器减一");
}).start();

// 创建第二个线程,也是完成某项任务后调用countDown方法
new Thread(() -> {
    System.out.println("线程2执行...");
    latch.countDown();
    System.out.println("线程2完成操作,计数器减一");
}).start();

try {
    // 调用await方法的线程会被阻塞,直到计数器的值变为0
    latch.await();
    System.out.println("两个线程的操作均已完成,主线程继续执行");
} catch (InterruptedException e) {
    e.printStackTrace();
}
CyclicBarrier

工作机制:允许一组线程相互等待到达一个共同屏障点

重要特性:

  • 屏障:允许提供一个 Runnable 任务,在所有线程都到达屏障,线程释放前执行该任务。通常用于合并最终结果或者进行某种必须等到所有线程都到达屏障点后才能执行的操作
  • 等待线程数:在创建 CyclicBarrier 时,需要指定等待的线程数量。当指定数量的线程都调用 await()方法,表示它们都到达了屏障点,随后这些线程都将被释放
  • 超时与中断:线程在调用 await()方法时可以选择设置超时时间,超时或者被中断都将导致线程提前释放,并抛出相应异常
  • 重置:释放等待线程后重置计数器。

示例:当四个线程都达到屏障后,打印一句话,然后每个线程继续执行它们的任务

java 复制代码
public class CyclicBarrierExample {
    // 创建一个新的CyclicBarrier,当四个参与者到达时执行屏障操作
    private CyclicBarrier barrier = new CyclicBarrier(4, () -> System.out.println("所有线程到达屏障点,屏障操作执行!"));

    public void startTask(String name) {
        new Thread(() -> {
            System.out.println(name + "开始执行任务...");
            // 模拟任务耗时
            try {
                Thread.sleep((int)(Math.random() * 1000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(name + "到达屏障点,等待其他线程...");
            try {
                // 调用await方法等待其他线程都到达屏障点
                barrier.await();
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println(name + "继续执行后续操作");
        }).start();
    }

    public static void main(String[] args) {
        CyclicBarrierExample example = new CyclicBarrierExample();
        example.startTask("线程A");
        example.startTask("线程B");
        example.startTask("线程C");
        example.startTask("线程D"); // 当所有四个线程达到屏障点,将一起释放,然后执行屏障操作
    }
}

线程交叉打印模版

java 复制代码
public class CrossPrinter {

    private int state;
    private final int printCount;

    public CrossPrinter(int printCount) {
        // state用来确定下次打印
        this.state = 0;
        // 打印次数
        this.printCount = printCount;
    }

    public void printLetter(String Letter, int crossState ,int curState) {
        for (int i = 0; i < printCount; i++) {
            synchronized (this) {
                while (state % crossState != curState) {
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(Thread.currentThread().getName() + ":" + Letter);
                state++;
                notifyAll();
            }
        }
    }

    public static void main(String[] args) {
        CrossPrinter crossPrinter = new CrossPrinter(5);

        // Thread A打印"A"
        new Thread(() -> crossPrinter.printLetter("A", 2,0), "Thread A").start();

        // Thread B打印"B"
        new Thread(() -> crossPrinter.printLetter("B", 2,1), "Thread B").start();
    }

}

上述完成了两线程交叉打印"A"、"B",具体说明下

  • printCount:控制交叉打印次数
  • state:全局变量,指明线程已经执行多少次了
  • crossState:指明有多少个线程进行交叉
  • curState:指明当前线程
  • Letter:当前线程打印内容

可用于:

  • 多线程交叉打印 A、B、C...
  • 两线程交叉打印奇偶数
三线程交叉打印 A、B、C

模版中是两线程交叉打印 A、B,只需要做简单替换就能实现三线程交叉打印 A、B、C

crossState:3

新增线程 C 如下

java 复制代码
// Thread A打印"A"
new Thread(() -> crossPrinter.printLetter("A", 3,0), "Thread A").start();

// Thread B打印"B"
new Thread(() -> crossPrinter.printLetter("B", 3,1), "Thread B").start();

// Thread C打印"C"
new Thread(() -> crossPrinter.printLetter("C", 3,2), "Thread C").start();
两线程交叉打印奇偶数

比如要求打印到两线程交叉打印到 10

state 控制线程进行轮次,此时可以换为 while 条件,用来控制跳出循环

crossState:2,表示两线程

完整代码如下:

java 复制代码
public class CrossPrinter {

    private int state;
    private final int printCount;

    public CrossPrinter(int printCount) {
        // state用来确定下次打印
        this.state = 0;
        // printCount表示打印次数
        this.printCount = printCount;
    }

    public void printNumber(int crossState ,int curState) {
        while (state < printCount) {
            synchronized (this) {
                while (state % crossState != curState) {
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(Thread.currentThread().getName() + ":" + state);
                state++;
                notifyAll();
            }
        }
    }

    public static void main(String[] args) {
        CrossPrinter crossPrinter = new CrossPrinter(10);

        // Thread A打印偶数
        new Thread(() -> crossPrinter.printNumber(2,0), "Thread A").start();

        // Thread B打印奇数
        new Thread(() -> crossPrinter.printNumber(2,1), "Thread B").start();
    }

}
三线程交叉打印斐波那契数列

新增 oneNum、twoNum 来记录前两个数

完整代码如下

java 复制代码
public class CrossPrinter {

    private int state;
    private int oneNum;
    private int twoNum;
    private final int printCount;


    public CrossPrinterThree(int printCount) {
        this.state = 3;
        this.oneNum = 1;
        this.twoNum = 1;
        this.printCount = printCount;
    }

    public static void main(String[] args) {
        CrossPrinterThree crossPrinterThree = new CrossPrinter(10);

        // 三线程交叉打印斐波那契数列
        new Thread(() -> crossPrinterThree.printNum(3, 0), "Thread-A").start();
        new Thread(() -> crossPrinterThree.printNum(3, 1), "Thread-B").start();
        new Thread(() -> crossPrinterThree.printNum(3, 2), "Thread-C").start();
    }

    private void printNum(int crossState, int curState) {
        while (state < printCount) {
            synchronized (this) {
                while (state % crossState != curState) {
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                int curNum = oneNum + twoNum;
                System._out_.println(Thread._currentThread_().getName() + ":" + curNum);

                // 更新前两个数
                oneNum = twoNum;
                twoNum = curNum;

                state++;
                notifyAll();
            }
        }
    }

}

多线程任务执行 A -> B, A -> C

实现方案:CountDownLatch

  • 为线程 B、C 分别设置 CountDownLatch 锁,当线程 A 执行后,唤醒线程 B、C 的 CountDownLatch 锁
java 复制代码
public class MultiThreadTaskExecution {

    // 使用两个初始计数为1的CountDownLatch来实现一对多的通知机制
    private CountDownLatch latchToB = new CountDownLatch(1);
    private CountDownLatch latchToC = new CountDownLatch(1);

    public void taskA() {
        System.out.println("任务A执行中...");
        try {
            Thread.sleep(100); // 模拟任务A执行时间
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("任务A执行完毕,通知任务B、C开始执行...");
        latchToB.countDown();
        latchToC.countDown();
    }

    public void taskB() {
        try {
            latchToB.await();
            System.out.println("任务B执行中...");
            Thread.sleep(100); // 模拟任务B执行时间
            System.out.println("任务B执行完毕...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void taskC() {
        try {
            latchToC.await();
            System.out.println("任务C执行中...");
            Thread.sleep(100); // 模拟任务C执行时间
            System.out.println("任务C执行完毕...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        MultiThreadTaskExecution taskExecution = new MultiThreadTaskExecution();
        new Thread(taskExecution::taskB).start();
        new Thread(taskExecution::taskC).start();
        new Thread(taskExecution::taskA).start();
    }

}

线程 A、B、C 都到达屏障点才执行后续操作

实现方案:CyclicBarrier

  • 设置屏障数量 3,同时可设置一个 Runnable 任务,当都达到时输出一句话。
java 复制代码
public class CyclicBarrierOne {

    // 创建一个新的CyclicBarrier,当3个参与者到达时执行屏障操作
    private CyclicBarrier barrier = new CyclicBarrier(3, () -> System._out_.println("所有线程到达屏障点,屏障操作执行!"));

    public static void main(String[] args) {
        CyclicBarrierOne cyclicBarrierOne = new CyclicBarrierOne();

        new Thread(() -> cyclicBarrierOne.startTask(), "Thread-A").start();
        new Thread(() -> cyclicBarrierOne.startTask(), "Thread-B").start();
        new Thread(() -> cyclicBarrierOne.startTask(), "Thread-C").start();

    }

    private void startTask() {
        System._out_.println(Thread._currentThread_().getName() + "开始执行任务...");
        try {
            Thread._sleep_(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System._out_.println(Thread._currentThread_().getName() + "到达屏障点,等待其他线程...");

        try {
            barrier.await();
        } catch (Exception e) {
            e.printStackTrace();
        }
        System._out_.println(Thread._currentThread_().getName() + "继续执行后续操作");

    }

}

10 个线程同时启动

java 复制代码
public class SimultaneousStart {

    private static final int _N _= 10;

    _// 创建一个CountDownLatch用于线程启动的信号_
_    _private static final CountDownLatch _startSignal _= new CountDownLatch(1);
    _// 创建一个 CountDownLatch 用于等待所有线程完成的信号_
_    _private static final CountDownLatch _doneSignal _= new CountDownLatch(_N_);

    public static void main(String[] args) throws InterruptedException {
        Runnable task = () -> {
            try {
                _startSignal_.await(); _// 等待启动信号_
_                _System._out_.println(Thread._currentThread_().getName() + " has started");
                Thread._sleep_(2000); _// 模拟任务执行_
_                _System._out_.println(Thread._currentThread_().getName() + " has finished");
            } catch (InterruptedException e) {
                Thread._currentThread_().interrupt();
            } finally {
                _doneSignal_.countDown(); _// 完成信号_
_            _}
        };

        _// 创建并启动N个线程_
_        _for (int i = 0; i < _N_; i++) {
            new Thread(task, "Thread-" + (i + 1)).start();
        }
        _// 主线程等待片刻,确保所有线程已经启动并在等待_
_        _Thread._sleep_(1000);

        System._out_.println("All threads are ready, starting now!");
        _startSignal_.countDown(); _// 发出启动信号_
_        doneSignal_.await(); _// 等待所有线程完成_

_        _System._out_.println("All threads have finished executing.");
    }
}

死锁

java 复制代码
public class DeadlockExample {

// 创建两个资源
private static final Object _resourceOne _= new Object();
private static final Object _resourceTwo _= new Object();


public static void main(String[] args) {
    new Thread(() -> {
        synchronized (resourceOne) {
            System.out.println(Thread.currentThread().getName() + "locked resource1");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (resourceTwo) {
                System.out.println(Thread.currentThread().getName() + "locked resource2");
            }
        }
    }, "Thread-A").start();

    new Thread(() -> {
        synchronized (resourceTwo) {
            System.out.println(Thread.currentThread().getName() + "locked resource2");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (resourceOne) {
                System.out.println(Thread.currentThread().getName() + "locked resource1");
            }
        }
    }, "Thread-B").start();

}

}

多个线程同时争抢同一把锁,阻塞情况下唤醒指定线程

  • 自定义条件变量
  • 标志变量

自定义条件变量

java 复制代码
public class CustomLockExample {
    private final Lock lock = new ReentrantLock();

    private final Condition conditionA = lock.newCondition();
    private final Condition conditionB = lock.newCondition();

    private void methodA() throws InterruptedException {
        lock.lock();
        try {
            System._out_.println("Thread A is waiting");
            conditionA.await();
            System._out_.println("Thread A is resumed");
        } finally {
            lock.unlock();
        }
    }

    private void methodB() throws InterruptedException {
        lock.lock();
        try {
            System._out_.println("Thread B is waiting");
            conditionB.await();
            System._out_.println("Thread B is resumed");
        } finally {
            lock.unlock();
        }
    }

    private void resumeA() {
        lock.lock();
        try {
            conditionA.signal();  _// Wake up one thread waiting on conditionA_
_            _System._out_.println("Signaled Thread A");
        } finally {
            lock.unlock();
        }
    }

    private void resumeB() {
        lock.lock();
        try {
            conditionB.signal();  _// Wake up one thread waiting on conditionB_
_            _System._out_.println("Signaled Thread B");
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        CustomLockExample example = new CustomLockExample();
        Thread threadA = new Thread(() -> {
            try {
                example.methodA();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        Thread threadB = new Thread(() -> {
            try {
                example.methodB();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        threadA.start();
        threadB.start();

        Thread._sleep_(2000);  _// Pause to ensure threads reach wait state_

_        _example.resumeA();   _// Signal threadA_
_        _Thread._sleep_(2000);
        example.resumeB();   _// Signal threadB_
_    _}
}

标志变量

java 复制代码
public class FlagBasedControl {
    private final Object lock = new Object();
    private volatile boolean isThreadAWake = false;

    private void methodA() throws InterruptedException {
        synchronized (lock) {
            while (!isThreadAWake) {
                System._out_.println("Thread A is waiting");
                lock.wait();
            }
        }
        System._out_.println("Thread A is resumed and resetting flag");
        isThreadAWake = false;  _// Reset the flag for next use    }_
_    _}

    private void resumeA() {
        synchronized (lock) {
            isThreadAWake = true;
            lock.notifyAll();  _// Wake up all threads, but only Thread A will proceed_
_            _System._out_.println("Signaled Thread A");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        FlagBasedControl example = new FlagBasedControl();

        Thread threadA = new Thread(() -> {
            try {
                example.methodA();
            } catch (InterruptedException e) {
                Thread._currentThread_().interrupt();
            }
        });

        threadA.start();

        Thread._sleep_(2000);  _// Pause to ensure thread reaches wait state_

_        _example.resumeA();  _// Signal threadA_
_    _}
}
相关推荐
赵渝强老师10 分钟前
【赵渝强老师】Memcached的路由算法
数据库·redis·nosql·memcached
belldeep23 分钟前
groovy 如何遍历 postgresql 所有的用户表 ?
数据库·postgresql
2401_8960081932 分钟前
PostgreSQL
数据库·postgresql
独行soc33 分钟前
2025年渗透测试面试题总结-百度面经(题目+回答)
运维·开发语言·经验分享·学习·面试·渗透测试·php
搞不懂语言的程序员41 分钟前
Redis Sentinel如何实现高可用?
数据库·redis·sentinel
wangzhongyudie1 小时前
SQL实战:06交叉日期打折问题求解
数据库·sql
caihuayuan52 小时前
生产模式下react项目报错minified react error #130的问题
java·大数据·spring boot·后端·课程设计
编程、小哥哥2 小时前
Java大厂面试:从Web框架到微服务技术的场景化提问与解析
java·spring boot·微服务·面试·技术栈·数据库设计·分布式系统
界面开发小八哥2 小时前
「Java EE开发指南」如何使用MyEclipse的可视化JSF编辑器设计JSP?(二)
java·ide·人工智能·java-ee·myeclipse
2401_841003982 小时前
mysql高可用
数据库·mysql