多线程编程题(面试题)超详细汇总与思考

省流总结:

多线程编程题要点
(1)熟悉如何创建线程、重写run()方法;
(2)如果多线程要对相同的资源进行操作访问,则需要加锁;
(3)如果线程间访问顺序有要求,或者有相互制约,则需要用到信号量。

前置知识:常见的几种创建线程写法

1.继承Thread类

继承Thread类,重写run()方法,用start()创线程并执行——面试时如果有多线程编程题,直接用Thread+匿名内部类写法就好,比较简单
在这里插入图片描述
在这里插入图片描述

2.实现Runnable接口

与继承Thread类写法类似:
在这里插入图片描述

3.实现Callable接口

对比前两者可以有返回值Future,常搭配线程池⼯具ExecutorService使用,ExecutorService 可以使⽤ submit ⽅法来让⼀个 Callable 接⼝执⾏。它会返回
⼀个 Future ,我们后续的程序可以通过这个 Future 的 get ⽅法得到结果。
较完整的写法是下面这样,但建议用的内部类的方式写(更简洁):
在这里插入图片描述
实现Callable接口的内部类写法案例——解决多线程卡死问题
思路:其实就是利用Future的get()方法去尝试获取线程执行结果,(get()是阻塞的,因此需要用重写的带有超时时间的get()方法,标蓝)如果获取超时则判定线程卡死,将任务取消+将线程关闭。

// 创建一个线程池
ExecutorService executor = Executors.newSingleThreadExecutor();
// 提交一个任务
Future<String> future = executor.submit(new Callable<String>() {
    @Override
    public String call() throws Exception {
        // 这里是你要执行的代码,比如br.readLine()
        return br.readLine();
    }
});
// 设置一个超时时间,比如5秒
long timeout = 5;
// 尝试获取任务的结果,如果超时就抛出异常
try {
    String s = future.get(timeout, TimeUnit.SECONDS);
    // 如果没有超时,就正常处理结果
    System.out.println(s);
} catch (TimeoutException e) {
    // 如果超时,就取消任务,并且处理异常
    future.cancel(true);
    System.out.println("Time out has occurred");
}
// 关闭线程池
executor.shutdown();

多线程编程题案例

1.两线程交替打印

思路:信号量+wait、notify机制即可
(1)使用信号量flag,信号量定两种状态——分别执行线程1、2;
(2)比如线程1先判断信号量是否是自己的状态,不是则wait()等待,如果是则执行(此时线程2在wait()),执行完修改信号量状态(仅两种状态,直接取反),然后notifyAll(),就能唤醒线程2,线程2执行…
(3)注意第(2)点的操作都要加synchronized锁。
在这里插入图片描述

2.三线程交替打印

跟上题类似,只是信号量flag需要定义三种状态,还需要规定三个线程执行顺序,比如线程1对应信号量指为1,他执行完后到线程2执行,则把flag修改为2,其他类似。
在这里插入图片描述

3.多个线程顺序执行

比如要求线程a执行完才开始线程b, 线程b执行完才开始线程。
直接用Join():
在这里插入图片描述

Join()原理补充:虽然是线程t1调用的join(),但其实真正执行的是main线程,简单说就是在t1还存活的时候main线程阻塞住(获取锁,lock.wait()),直到线程t1 die之后才notifyAll()

4.三个窗口卖票

因为有了“票”这个对象,担心多线程并发访问他,所以需要加锁,这题没有出售顺序的要求,因此不用信号量,只加锁就够了,当然用信号量+wait、notify应该也是可以实现的。
注意因为这题有“票”这个物品,相对地加锁就可以是给Ticket对象加锁。
在这里插入图片描述

5.生产者消费者模型

跟上题类似,因为生产者、消费者会对“资源”这么个对象有操作,因此也需要锁,而且生产者和消费者有相互约束,还需要配合信号量。因此是锁+信号量的方式,对比上一题,没有“票”这样一个物品,所以需要额外定义一个对象锁(这里是用的字符串大写String),没想到String对象也含有wait、notify方法。
至于选用的锁,可以是synchronized,或者reentrantLock(有序),还有阻塞队列。
reentrantLock的写法稍微麻烦点,还可以用到Condition(强化wait、notify),详见参考博客。
这里只记录synchronized的写法:

public class FruitPlateDemo {

    private final static String LOCK = "lock";
    private int count = 0;
    private static final int FULL = 10;

    public static void main(String[] args) {
        FruitPlateDemo fruitDemo1 = new FruitPlateDemo();
        for (int i = 1; i <= 5; i++) {
            new Thread(fruitDemo1.new Producer(), "生产者-" + i).start();
            new Thread(fruitDemo1.new Consumer(), "消费者-" + i).start();
        }
    }

    class Producer implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                synchronized (LOCK) {
                    while (count == FULL) {
                        try {
                            LOCK.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }

                    System.out.println("生产者 " + Thread.currentThread().getName() + " 总共有 " + ++count + " 个资源");
                    LOCK.notifyAll();
                }
            }
        }
    }

    class Consumer implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                synchronized (LOCK) {
                    while (count == 0) {
                        try {
                            LOCK.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("消费者 " + Thread.currentThread().getName() + " 总共有 " + --count + " 个资源");
                    LOCK.notifyAll();
                }
            }
        }
    }
}

参考

https://blog.csdn.net/shinecjj/article/details/103792151

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值