博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java多线程经典的4道笔试题---含多种实现方式
阅读量:2496 次
发布时间:2019-05-11

本文共 16587 字,大约阅读时间需要 55 分钟。

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
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
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、阻塞队列,原子类,目的就是为了演示他们各自的实现方式以及区别,多线程编程是非常灵活的,你可以尝试再用这几种方式实现吗?

转载地址:http://golrb.baihongyu.com/

你可能感兴趣的文章
开源SoC整理
查看>>
【2020-3-21】Mac安装Homebrew慢,解决办法
查看>>
influxdb 命令行输出时间为 yyyy-MM-dd HH:mm:ss(年月日时分秒)的方法
查看>>
已知子网掩码,确定ip地址范围
查看>>
判断时间或者数字是否连续
查看>>
docker-daemon.json各配置详解
查看>>
Docker(一)使用阿里云容器镜像服务
查看>>
Docker(三) 构建镜像
查看>>
Spring 全家桶注解一览
查看>>
JDK1.8-Stream API使用
查看>>
cant connect to local MySQL server through socket /tmp/mysql.sock (2)
查看>>
vue中的状态管理 vuex store
查看>>
Maven之阿里云镜像仓库配置
查看>>
Maven:mirror和repository 区别
查看>>
微服务网关 Spring Cloud Gateway
查看>>
SpringCloud Feign的使用方式(一)
查看>>
SpringCloud Feign的使用方式(二)
查看>>
关于Vue-cli+ElementUI项目 打包时排除Vue和ElementUI
查看>>
Vue 路由懒加载根据根路由合并chunk块
查看>>
vue中 不更新视图 四种解决方法
查看>>