头条面试居然跟我扯了半小时的Semaphore,java框架面试题及答案

:有acquire方法和release方法。 当调用acquire方法时线程就会被阻塞,直到Semaphore中可以获得到许可证为止,然后线程再获取这个许可证。 当调用release方法时将向Semaphore中添加一个许可证,如果有线程因为获取许可证被阻塞时,它将获取到许可证并被释放;如果没有获取许可证的线程, Semaphore只是记录许可证的可用数量。

面试官:可以举一个使用Semaphore的例子吗?

:张三、李四和王五和赵六4个人一起去饭店吃饭,不过在特殊时期洗手很重要,饭前洗手也是必须的,可是饭店只有2个洗手池,洗手池就是不能被同时使用的公共资源,这种场景就可以用到Semaphore。

面试官:可以简单用代码实现一下吗?

:当然可以,这是张三、李四、王五和赵六的顾客类:

package onemore.study.semaphore;

import java.text.SimpleDateFormat;

import java.util.Date;

import java.util.Random;

import java.util.concurrent.CountDownLatch;

import java.util.concurrent.Semaphore;

public class Customer implements Runnable {

private Semaphore washbasin;

private String name;

public Customer(Semaphore washbasin, String name) {

this.washbasin = washbasin;

this.name = name;

}

@Override

public void run() {

try {

SimpleDateFormat sdf = new SimpleDateFormat(“HH:mm:ss.SSS”);

Random random = new Random();

washbasin.acquire();

System.out.println(

sdf.format(new Date()) + " " + name + " 开始洗手…");

Thread.sleep((long) (random.nextDouble() * 5000) + 2000);

System.out.println(

sdf.format(new Date()) + " " + name + " 洗手完毕!");

washbasin.release();

} catch (Exception e) {

e.printStackTrace();

}

}

}

然后,写一个测试类模拟一下他们洗手的过程:

package onemore.study.semaphore;

import java.util.ArrayList;

import java.util.List;

import java.util.concurrent.Semaphore;

public class SemaphoreTester {

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

//饭店里只用两个洗手池,所以初始化许可证的总数为2。

Semaphore washbasin = new Semaphore(2);

List threads = new ArrayList<>(3);

threads.add(new Thread(new Customer(washbasin, “张三”)));

threads.add(new Thread(new Customer(washbasin, “李四”)));

threads.add(new Thread(new Customer(washbasin, “王五”)));

threads.add(new Thread(new Customer(washbasin, “赵六”)));

for (Thread thread : threads) {

thread.start();

Thread.sleep(50);

}

for (Thread thread : threads) {

thread.join();

}

}

}

运行以后的结果应该是这样的:

06:51:54.416 李四 开始洗手…

06:51:54.416 张三 开始洗手…

06:51:57.251 张三 洗手完毕!

06:51:57.251 王五 开始洗手…

06:51:59.418 李四 洗手完毕!

06:51:59.418 赵六 开始洗手…

06:52:02.496 王五 洗手完毕!

06:52:06.162 赵六 洗手完毕!

可以看到,当已经有两个人在洗手的时候,其他人就被阻塞,直到有人洗手完毕才是开始洗手。

面试官:对Semaphore的内部原理有没有了解?

:Semaphore内部主要通过AQS(AbstractQueuedSynchronizer)实现线程的管理。Semaphore在构造时,需要传入许可证的数量,它最后传递给了AQS的state值。线程在调用acquire方法获取许可证时,如果Semaphore中许可证的数量大于0,许可证的数量就减1,线程继续运行,当线程运行结束调用release方法时释放许可证时,许可证的数量就加1。如果获取许可证时,Semaphore中许可证的数量为0,则获取失败,线程进入AQS的等待队列中,等待被其它释放许可证的线程唤醒。

面试官:嗯,不错。在您的代码中,这4个人会按照线程启动的顺序洗手嘛?

:不会按照线程启动的顺序洗手,有可能赵六比王五先洗手。

面试官:为什么会出现这种情况?

:因为在我的代码中,使用Semaphore的构造函数是这个:

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
img

最后

小编利用空余时间整理了一份《MySQL性能调优手册》,初衷也很简单,就是希望能够帮助到大家,减轻大家的负担和节省时间。

关于这个,给大家看一份学习大纲(PDF)文件,每一个分支里面会有详细的介绍。

image

这里都是以图片形式展示介绍,如要下载原文件以及更多的性能调优笔记(MySQL+Tomcat+JVM)!
img-JHdbBPlg-1711852266734)]

最后

小编利用空余时间整理了一份《MySQL性能调优手册》,初衷也很简单,就是希望能够帮助到大家,减轻大家的负担和节省时间。

关于这个,给大家看一份学习大纲(PDF)文件,每一个分支里面会有详细的介绍。

[外链图片转存中…(img-IGJu2R8J-1711852266734)]

这里都是以图片形式展示介绍,如要下载原文件以及更多的性能调优笔记(MySQL+Tomcat+JVM)!

  • 7
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,以下是Java基础面试题答案: 1. Java 中的四种访问修饰符分别是什么?它们的作用是什么? 答:Java 中的四种访问修饰符分别是 public、private、protected 和 default。它们的作用如下: - public:可以被任何类访问。 - private:只能在本类内部访问。 - protected:可以被本类、子类和同一个包中的其他类访问。 - default:只能被同一个包中的其他类访问,不加访问修饰符时默认为 default。 2. Java 中的 final 关键字有哪些用法? 答:Java 中的 final 关键字有以下三种用法: - 修饰类:表示该类不能被继承。 - 修饰方法:表示该方法不能被子类重写。 - 修饰变量:表示该变量的值不能被修改,常用于常量的定义。 3. Java 中的抽象类和接口有什么区别? 答:Java 中的抽象类和接口都不能被实例化,但它们有以下区别: - 抽象类可以包含非抽象方法,而接口中只能包含抽象方法。 - 子类继承抽象类时必须实现其抽象方法,而实现接口时必须实现接口中的所有方法。 - 一个类只能继承一个抽象类,但可以实现多个接口。 4. 请举例说明 Java 中的多态是如何实现的? 答:Java 中的多态是通过继承和方法重写实现的。当一个子类继承自父类并重写了父类的某个方法时,在使用子类对象调用该方法时,会根据实际运行时对象的类型来确定调用哪个方法,即实现了动态绑定,这就是多态。 5. Java 中的异常处理机制是什么?请介绍一下常见的异常类型和它们的作用。 答:Java 中的异常处理机制是通过 try-catch-finally 语句块实现的。常见的异常类型有: - RuntimeException:运行时异常,如空指针异常、数组越界异常等。 - IOException:输入输出异常,如文件读写异常等。 - ClassNotFoundException:类不存在异常,如通过 Class.forName() 加载类时找不到该类。 - InterruptedException:线程中断异常,如线程在等待时被中断。 - SQLException:SQL 异常,如连接数据库失败、执行 SQL 语句失败等。 6. 什么是 Java 中的泛型?泛型有什么作用?请举例说明。 答:Java 中的泛型是一种类型参数化的机制,可以让程序在编译时检查类型安全性,并且可以在运行时不需要进行类型转换。泛型的作用是可以让代码更加通用、类型安全,并且可以减少代码中的强制类型转换。例如,List<String> list = new ArrayList<String>(); 表示创建一个只能存放字符串类型的列表。 7. 请介绍一下 Java 中的集合框架,包括常用的集合类和它们的特点。 答:Java 中的集合框架包括 Collection 和 Map 两个接口和它们的实现类。常用的集合类有: - ArrayList:基于数组实现的可变长度列表,支持随机访问。 - LinkedList:基于链表实现的可变长度列表,支持高效的插入和删除操作。 - HashSet:基于哈希表实现的无序集合,不允许重复元素。 - TreeSet:基于红黑树实现的有序集合,不允许重复元素。 - HashMap:基于哈希表实现的无序键值对集合,不允许重复的 key。 - TreeMap:基于红黑树实现的有序键值对集合,不允许重复的 key。 8. Java 中的线程是什么?如何创建和启动一个线程?请介绍一下常用的线程同步方式。 答:Java 中的线程是程序执行的单元,可以同时执行多个任务。可以通过继承 Thread 类或实现 Runnable 接口来创建线程,并通过 start() 方法启动线程。常用的线程同步方式有: - synchronized 关键字:可以对代码块或方法进行加锁,使得同一时刻只有一个线程可以进入临界区。 - Lock 接口:是 JDK1.5 引入的新特性,可以通过 ReentrantLock 类来实现锁定。 - Semaphore 类:可以控制同时访问临界区的线程数量。 - CountDownLatch 类:一个线程等待多个线程完成任务后再执行。 9. 请介绍一下 Java 中的反射机制,以及它的应用场景。 答:Java 中的反射机制是指在运行时动态地获取一个类的信息,并且可以在运行时操作对象。反射机制的应用场景有: - 运行时动态加载类和创建对象。 - 对象的属性和方法的动态操作。 - 动态代理和 AOP 技术的实现。 - 注解的解析和处理。 10. 什么是 Java 中的 MVC 设计模式?请举例说明其在实际开发中的应用。 答:Java 中的 MVC 设计模式是指将一个应用程序分为 Model(模型)、View(视图)和 Controller(控制器)三个部分,使得三个部分可以相互独立,降低了耦合性。在实际开发中,我们可以通过 Spring MVC 框架来实现 MVC 模式,其中 Model 表示数据模型,View 表示用户界面,Controller 表示控制器。例如,当用户访问一个 URL 时,Controller 会接收这个请求,根据请求的参数和业务逻辑来调用相应的 Service 方法,并将返回的数据存储到 Model 中,最后将 Model 中的数据传递给 View 进行渲染。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值