Java多线程经典的4道笔试题---含多种实现方式

1、按序输出

同时启动三个线程,分别为A、B、C,其中A线程打印“a”,B线程打印“b”,C线程打印“c”,要求按照abc顺序打印输出。

第一种方式

使用join实现,join方法表示调用线程必须等待该线程执行完成后才能继续往下执行。

public class SortPrint {
    public static void main(String[] args) throws InterruptedException {

        SortPrint sortPrint = new SortPrint();

        Thread ta = new Thread(() -> sortPrint.A());
        Thread tb = new Thread(() -> sortPrint.B());
        Thread tc = new Thread(() -> sortPrint.C());

        ta.start();
        ta.join();

        tb.start();
        tb.join();

        tc.start();
    }

    public void A() {
        System.out.println("a");
    }

    public void B() {
        System.out.println("b");
    }

    public void C() {
        System.out.println("c");
    }
}

实际上join方法并不满足同时启动三个线程的要求,不过join方法是保证线程执行顺序的一种简单的方式,所以还是拿出来演示一下。

第二种方式

使用同步的方式,通过notifyAll和wait方法实现线程间的通信。

public class SortPrint_V2 {

    static String flag = "A";

    public static void main(String[] args) {
        SortPrint_V2 sortPrint_v2 = new SortPrint_V2();
        Thread ta = new Thread(() -> sortPrint_v2.A());
        Thread tb = new Thread(() -> sortPrint_v2.B());
        Thread tc = new Thread(() -> sortPrint_v2.C());

        ta.start();

        tb.start();

        tc.start();
    }

    public synchronized void A() {
        try {
            while (true) {
                if (flag.equals("A")) {
                    System.out.println("a");
                    flag = "B";
                    this.notifyAll();
                } else {
                    this.wait();
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public synchronized void B() {
        try {
            while (true) {
                if (flag.equals("B")) {
                    System.out.println("b");
                    flag = "C";
                    this.notifyAll();
                } else {
                    this.wait();
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public synchronized void C() {
        try {
            while (true) {
                if (flag.equals("C")) {
                    System.out.println("c");
                    flag = "A";
                    this.notifyAll();
                } else {
                    this.wait();
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

2、交替打印

同时启动两个线程,分别为A、B,其中A线程打印“a”,B线程打印“b”,要求按照ababab…的顺序交替打印输出。

第一种方式

使用Semaphore的方式,先给A线程初始化一个通行证,A线程输出完之后给B线程一个通行证,B线程输出完之后再给A一个通行证,以此反复,实现需求。

public class TurnPrint {

    Semaphore semaphore1 = new Semaphore(1);
    Semaphore semaphore2 = new Semaphore(0);

    public static void main(String[] args) {
        TurnPrint turnPrint = new TurnPrint();

        Thread ta = new Thread(() -> turnPrint.A());
        Thread tb = new Thread(() -> turnPrint.B());

        ta.start();
        tb.start();

    }

    public void A() {
        try {
            while (true) {
                semaphore1.acquire();
                System.out.println("a");
                semaphore2.release();

            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void B() {
        try {
            while (true) {
                semaphore2.acquire();
                System.out.println("b");
                semaphore1.release();

            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

第二种方式

和第一道题一样,使用notifyAll和wait实现。

public class TurnPrint_V2 {

    static String flag = "A";

    public static void main(String[] args) {
        TurnPrint_V2 turnPrint = new TurnPrint_V2();

        Thread ta = new Thread(() -> turnPrint.A());
        Thread tb = new Thread(() -> turnPrint.B());

        ta.start();
        tb.start();

    }

    public synchronized void A() {
        try {
            while (true) {
                if (flag.equals("A")) {
                    System.out.println("a");
                    flag = "B";
                    this.notify();
                } else {
                    this.wait();
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public synchronized void B() {
        try {
            while (true) {
                if (flag.equals("B")) {
                    System.out.println("b");
                    flag = "A";
                    this.notify();
                } else {
                    this.wait();
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

以上两道题基本上一个意思,几种方法都能实现这两个需求。

实现生产者与消费者

1、notifyAll和wait实现版

public class ProducerAndConsumer {

    public static void main(String[] args) {

        //让生产者、消费者共享同一个商品工厂
        GoodsFactory goodsFactory = new GoodsFactory();

        Producer producer = new Producer(goodsFactory);
        producer.start();

        Consumer consumer = new Consumer(goodsFactory);
        consumer.start();
    }
}

/**
 * 商品工厂,负责生产和消费商品
 */
class GoodsFactory {
    AtomicInteger atomicInteger = new AtomicInteger();
    List<Goods> goodsList = new ArrayList<>();

    public synchronized void consumerGoods() throws InterruptedException {
        if (goodsList.size() <= 0) {
            System.out.println("商品消费完了,等待生产!");
            //商品消费完了以后,释放锁并阻塞自己,等待唤醒的通知。
            this.wait();
        } else {
            Goods goods = goodsList.remove(0);
            System.out.println("消费了一个商品:" + goods);
            //消费了一个商品后,唤醒生产者
            this.notifyAll();
        }
    }

    public synchronized void producerGoods() throws InterruptedException {
        if (goodsList.size() >= 5) {
            System.out.println("商品太多了,停止生产,等待被消费!");
            //商品满了以后,释放锁并阻塞自己,等待唤醒的通知。
            this.wait();
        } else {
            Goods goods = new Goods(atomicInteger.getAndIncrement(), "xxoo");
            goodsList.add(goods);
            System.out.println("生产了一个商品:" + goods);
            //生产了一个商品后,唤醒消费者
            this.notifyAll();
        }
    }
}

/**
 * 消费者线程,调用商品工厂消费商品
 */
class Consumer extends Thread {

    GoodsFactory goodsFactory;

    public Consumer(GoodsFactory goodsFactory) {
        this.goodsFactory = goodsFactory;
    }

    @Override
    public void run() {
        try {
            while (true) {
                Thread.sleep(new Random().nextInt(10) * 100);
                goodsFactory.consumerGoods();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

/**
 * 生产者线程,调用商品工厂生产商品
 */
class Producer extends Thread {

    GoodsFactory goodsFactory;

    public Producer(GoodsFactory goodsFactory) {
        this.goodsFactory = goodsFactory;
    }

    @Override
    public void run() {
        try {
            while (true) {
                Thread.sleep(new Random().nextInt(10) * 100);
                goodsFactory.producerGoods();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}


class Goods {

    private int goodId;

    private String goodName;

    public Goods(int goodId, String goodName) {
        this.goodId = goodId;
        this.goodName = goodName;
    }

    @Override
    public String toString() {
        return "Goods{" +
                "goodId=" + goodId +
                ", goodName='" + goodName + '\'' +
                '}';
    }
}

2、基于阻塞队列BlockingDeque实现版

完全依赖阻塞队列自身的API帮我们实现,非常简单。

只需替换上一版的GoodsFactory ,其他不变

class GoodsFactory {

    AtomicInteger atomicInteger = new AtomicInteger();

    LinkedBlockingDeque<Goods> goodsList = new LinkedBlockingDeque<>(5);

    public void consumerGoods() throws InterruptedException {
        Goods goods = goodsList.take();
        System.out.println("消费了一个商品:" + goods + ",当前还有" + goodsList.size() + "件商品");
    }

    public void producerGoods() throws InterruptedException {
        Goods goods = new Goods(atomicInteger.getAndIncrement(), "xxoo");
        goodsList.put(goods);
        System.out.println("生产了一个商品:" + goods + ",当前还有" + goodsList.size() + "件商品");
    }
}

4、哲学家进餐

在这里插入图片描述

第一位哲学家和第二位哲学家 共享1号叉子。
第二位哲学家和第三位哲学家 共享2号叉子。
第三位哲学家和第四位哲学家 共享3号叉子。
第四位哲学家和第五位哲学家 共享4号叉子。
第五位哲学家和第一位哲学家 共享5号叉子。

哲学家进餐是一个经典的描述多线程情况下出现死锁的问题,比如下面这段代码按照正常的逻辑实现,就会有死锁问题的产生。

public class DiningPhilosophers {

    public static void main(String[] args) {

        Fork f1 = new Fork("1");
        Fork f2 = new Fork("2");
        Fork f3 = new Fork("3");
        Fork f4 = new Fork("4");
        Fork f5 = new Fork("5");

        new Thread(new Philosophers("1号哲学家", f1, f5)).start();
        new Thread(new Philosophers("2号哲学家", f2, f1)).start();
        new Thread(new Philosophers("3号哲学家", f3, f2)).start();
        new Thread(new Philosophers("4号哲学家", f4, f3)).start();
        new Thread(new Philosophers("5号哲学家", f5, f4)).start();

    }

}

class Philosophers extends Thread {

    private Fork leftFork;
    private Fork rightFork;

    private String name;

    public Philosophers(String name, Fork leftFork, Fork rightFork) {
        this.name = name;
        this.leftFork = leftFork;
        this.rightFork = rightFork;
    }

    @Override
    public void run() {
        while(true){
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            dining();
            thinking();
        }
    }

    private void thinking() {
        System.out.println(name + "正在思考");
    }

    private void dining() {
        synchronized (leftFork) {

            System.out.println(name + leftFork);

            synchronized (rightFork) {

                System.out.println(name + rightFork);

                System.out.println(name + "正在用餐");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }
    }
}

class Fork {
    private String name;

    public Fork(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Fork{" +
                "name='" + name + '\'' +
                '}';
    }
}

每个哲学家都拿起了自己左边的叉子,并等待右边的叉子。。。

在这里插入图片描述

要解决的死锁的问题一般只要打破产生死锁的4个必要条件的其中一个即可解决。理解死锁产生的四个必要条件

1、通过一个助理类辅助实现,破坏死锁中请求不释放的条件。

import java.util.concurrent.atomic.AtomicInteger;

public class DiningPhilosophers {

    public static void main(String[] args) {

        PhilosophersAssistant philosophersAssistant = new PhilosophersAssistant();

        Fork f1 = new Fork("1");
        Fork f2 = new Fork("2");
        Fork f3 = new Fork("3");
        Fork f4 = new Fork("4");
        Fork f5 = new Fork("5");

        new Thread(new Philosophers("第1位哲学家", f1, f5, philosophersAssistant)).start();
        new Thread(new Philosophers("第2位哲学家", f2, f1, philosophersAssistant)).start();
        new Thread(new Philosophers("第3位哲学家", f3, f2, philosophersAssistant)).start();
        new Thread(new Philosophers("第4位哲学家", f4, f3, philosophersAssistant)).start();
        new Thread(new Philosophers("第5位哲学家", f5, f4, philosophersAssistant)).start();

    }

}

/**
 * 助手,帮助哲学家记录当前拿起了左边叉子的人数
 */
class PhilosophersAssistant {
    AtomicInteger atomicInteger = new AtomicInteger();
}

class Philosophers extends Thread {

    PhilosophersAssistant philosophersAssistant;

    private Fork leftFork;
    private Fork rightFork;

    private String name;

    public Philosophers(String name, Fork leftFork, Fork rightFork, PhilosophersAssistant philosophersAssistant) {
        this.name = name;
        this.leftFork = leftFork;
        this.rightFork = rightFork;
        this.philosophersAssistant = philosophersAssistant;
    }

    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            dining();
            thinking();
        }
    }

    private void thinking() {
        System.out.println(name + "正在思考");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void dining() {
        synchronized (leftFork) {
            int andIncrement = philosophersAssistant.atomicInteger.getAndIncrement();
            //最多只能同时4个科学家拿起左边的叉子
            if (andIncrement == 4) {
                //主动释放自己当前拿起的左边的叉子
                System.out.println(name + "放下了" + leftFork.getName() + "号叉子");
                return;
            }

            System.out.println(name + "拿起了" + leftFork.getName() + "号叉子" + ",等待" + rightFork.getName() + "号叉子");

            synchronized (rightFork) {

                System.out.println(name + "拿起了" + rightFork.getName() + "号叉子," + "开始用餐");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                philosophersAssistant.atomicInteger.decrementAndGet();
            }
        }
    }
}

class Fork {
    private String name;

    public Fork(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

2、使用lock方式实现,同样破坏死锁中请求不释放的条件。

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

public class DiningPhilosophers {

    public static void main(String[] args) {

        ReentrantLock r1 = new ReentrantLock();
        ReentrantLock r2 = new ReentrantLock();
        ReentrantLock r3 = new ReentrantLock();
        ReentrantLock r4 = new ReentrantLock();
        ReentrantLock r5 = new ReentrantLock();

        Fork f1 = new Fork("1");
        Fork f2 = new Fork("2");
        Fork f3 = new Fork("3");
        Fork f4 = new Fork("4");
        Fork f5 = new Fork("5");

        new Thread(new Philosophers("第1位哲学家", f1, f5, r1, r5)).start();
        new Thread(new Philosophers("第2位哲学家", f2, f1, r2, r1)).start();
        new Thread(new Philosophers("第3位哲学家", f3, f2, r3, r2)).start();
        new Thread(new Philosophers("第4位哲学家", f4, f3, r4, r3)).start();
        new Thread(new Philosophers("第5位哲学家", f5, f4, r5, r4)).start();

    }

}

class Philosophers extends Thread {

    private Fork leftFork;
    private Fork rightFork;
    private String name;
    private ReentrantLock leftLock;
    private ReentrantLock rightLock;

    public Philosophers(String name, Fork leftFork, Fork rightFork, ReentrantLock leftLock, ReentrantLock rightLock) {
        this.name = name;
        this.leftFork = leftFork;
        this.rightFork = rightFork;
        this.leftLock = leftLock;
        this.rightLock = rightLock;
    }

    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(10);
                dining();
                thinking();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private void thinking() {
        System.out.println(name + "正在思考");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void dining() {
        try {
            boolean tryLock = leftLock.tryLock(1, TimeUnit.SECONDS);
            if (!tryLock) {
                System.out.println(name + "尝试拿起" + leftFork.getName() + "号叉子失败");
                return;
            }
            System.out.println(name + "拿起了" + leftFork.getName() + "号叉子" + ",等待" + rightFork.getName() + "号叉子");
            tryLock = rightLock.tryLock(1, TimeUnit.SECONDS);
            if (!tryLock) {
                System.out.println(name + "放下了" + leftFork.getName() + "号叉子");
                leftLock.unlock();
                return;
            }
            try {
                System.out.println(name + "拿起了" + rightFork.getName() + "号叉子," + "开始用餐");
                Thread.sleep(2000);
                System.out.println(name + "用餐结束,放下了" + leftFork.getName() + "和" + rightFork.getName() + "号叉子");
            } finally {
                leftLock.unlock();
                rightLock.unlock();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class Fork {
    private String name;

    public Fork(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

}

以上4道都是比较常见的笔试题,我刻意分别用了synchronized、lock、Semaphore、阻塞队列,原子类,目的就是为了演示他们各自的实现方式以及区别,多线程编程是非常灵活的,你可以尝试再用这几种方式实现吗?

  • 2
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码拉松

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值