校招Java岗位面经(一面)

面试题是Guide哥那里找的,我帮大家找来答案并且整理了一下。

大家背八股文可以去看Guide哥的。JavaGuide(Java学习&&面试指南) | JavaGuide(Java面试+学习指南)

这是携程的Java一面。


📣一面45min

题目:

  1. 进程和线程的区别:

  1. 并行和并发的区别:

  1. synchronized的作用:

  1. synchronized和ReentrantLock的区别,如何选择:

  1. ThreadLocal使用过程中可能存在的问题:

  1. ThreadLocal内存泄露问题是怎么导致的:

  1. 项目中是如何创建线程池的,什么不用Executors去创建线程池:

  1. 知道本地的缓存,选择Caffeine的原因:

  1. Redis这类缓存和Caffeine的区别:

  1. Redis中常见的数据结构,应用场景:

  1. 缓存穿透和缓存雪崩的区别,解决办法:

  1. MySQL和Redis怎么保持数据一致:

  1. 一个SQL笔试题,join多表查询


答案:

1️⃣进程和线程的区别:

🔑:线程具有许多传统进程所具有的特征,故又称为轻型进程(Light—Weight Process)或进程元;而把传统的进程称为重型进程(Heavy—Weight Process),它相当于只有一个线程的任务。在引入了线程的操作系统中,通常一个进程都有若干个线程,至少包含一个线程。

根本区别:进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位

资源开销:每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小。

包含关系:如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的;线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程。

内存分配:同一进程的线程共享本进程的地址空间和资源,而进程之间的地址空间和资源是相互独立的

影响关系:一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。

执行过程:每个独立的进程有程序运行的入口、顺序执行序列和程序出口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制,两者均可并发执行

2️⃣并行和并发的区别:

🔑:

  • 并发:两个及两个以上的作业在同一 时间段 内执行。

  • 并行:两个及两个以上的作业在同一 时刻 执行。

3️⃣synchronized的作用:

🔑:synchronized 是 Java 中的一个关键字,翻译成中文是同步的意思,主要解决的是多个线程之间访问资源的同步性,可以保证被它修饰的方法或者代码块在任意时刻只能有一个线程执行。

在 Java 早期版本中,synchronized 属于 重量级锁,效率低下。这是因为监视器锁(monitor)是依赖于底层的操作系统的 Mutex Lock 来实现的,Java 的线程是映射到操作系统的原生线程之上的。如果要挂起或者唤醒一个线程,都需要操作系统帮忙完成,而操作系统实现线程之间的切换时需要从用户态转换到内核态,这个状态之间的转换需要相对比较长的时间,时间成本相对较高。

不过,在 Java 6 之后, synchronized 引入了大量的优化如自旋锁、适应性自旋锁、锁消除、锁粗化、偏向锁、轻量级锁等技术来减少锁操作的开销,这些优化让 synchronized 锁的效率提升了很多。因此, synchronized 还是可以在实际项目中使用的,像 JDK 源码、很多开源框架都大量使用了 synchronized 。

4️⃣synchronized和ReentrantLock的区别,如何选择:

🔑:

两者都是可重入锁

可重入锁 也叫递归锁,指的是线程可以再次获取自己的内部锁。比如一个线程获得了某个对象的锁,此时这个对象锁还没有释放,当其再次想要获取这个对象的锁的时候还是可以获取的,如果是不可重入锁的话,就会造成死锁。

JDK 提供的所有现成的 Lock 实现类,包括 synchronized 关键字锁都是可重入的。

在下面的代码中,method1() 和 method2()都被 synchronized 关键字修饰,method1()调用了method2()。

public class ReentrantLockDemo {
    public synchronized void method1() {
        System.out.println("方法1");
        method2();
    }

    public synchronized void method2() {
        System.out.println("方法2");
    }
}

由于 synchronized锁是可重入的,同一个线程在调用method1() 时可以直接获得当前对象的锁,执行 method2() 的时候可以再次获取这个对象的锁,不会产生死锁问题。假如synchronized是不可重入锁的话,由于该对象的锁已被当前线程所持有且无法释放,这就导致线程在执行 method2()时获取锁失败,会出现死锁问题。

5️⃣ThreadLocal使用过程中可能存在的问题:

🔑:内存泄露

6️⃣ThreadLocal内存泄露问题是怎么导致的:

🔑:

ThreadLocalMap 中使用的 key 为 ThreadLocal 的弱引用,而 value 是强引用。所以,如果 ThreadLocal 没有被外部强引用的情况下,在垃圾回收的时候,key 会被清理掉,而 value 不会被清理掉。

这样一来,ThreadLocalMap 中就会出现 key 为 null 的 Entry。假如我们不做任何措施的话,value 永远无法被 GC 回收,这个时候就可能会产生内存泄露。ThreadLocalMap 实现中已经考虑了这种情况,在调用 set()、get()、remove() 方法的时候,会清理掉 key 为 null 的记录。使用完 ThreadLocal方法后 最好手动调用remove()方法

static class Entry extends WeakReference<ThreadLocal<?>> {
    /** The value associated with this ThreadLocal. */
    Object value;

    Entry(ThreadLocal<?> k, Object v) {
        super(k);
        value = v;
    }
}

弱引用介绍:

如果一个对象只具有弱引用,那就类似于 可有可无的生活用品。弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它 所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程, 因此不一定会很快发现那些只具有弱引用的对象。
弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java 虚拟机就会把这个弱引用加入到与之关联的引用队列中。

7️⃣项目中是如何创建线程池的,什么不用Executors去创建线程池:

🔑:通过ThreadPoolExecutor构造函数来创建;

在《阿里巴巴 Java 开发手册》“并发处理”这一章节,明确指出线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。

为什么呢?

使用线程池的好处是减少在创建和销毁线程上所消耗的时间以及系统资源开销,解决资源不足的问题。如果不使用线程池,有可能会造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。

另外,《阿里巴巴 Java 开发手册》中强制线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 构造函数的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险

Executors 返回线程池对象的弊端如下:

  • FixedThreadPool 和 SingleThreadExecutor : 使用的是无界的 LinkedBlockingQueue,任务队列最大长度为 Integer.MAX_VALUE,可能堆积大量的请求,从而导致 OOM。

  • CachedThreadPool :使用的是同步队列 SynchronousQueue, 允许创建的线程数量为 Integer.MAX_VALUE ,可能会创建大量线程,从而导致 OOM。

  • ScheduledThreadPool 和 SingleThreadScheduledExecutor : 使用的无界的延迟阻塞队列DelayedWorkQueue,任务队列最大长度为 Integer.MAX_VALUE,可能堆积大量的请求,从而导致 OOM。

8️⃣知道本地的缓存,选择Caffeine的原因:

🔑:Caffeine Cache。它是站在巨人的肩膀上-Guava Cache,借着他的思想优化了算法发展而来。

选择它的原因是

1.Caffine Cache 在算法上的优点-W-TinyLFU;

2.Caffeine Cache提供了三种缓存填充策略:手动、同步加载和异步加载。

还有很多:推荐博客Caffeine Cache-高性能Java本地缓存组件 - rickiyang - 博客园 (cnblogs.com)

9️⃣Redis这类缓存和Caffeine的区别:

🔑:

相同点:两个都是缓存的方式

不同点:

redis是将数据存储到内存里

caffeine是将数据存储在本地应用里

caffeine和redis相比,没有了网络IO上的消耗

联系:一般将两者结合起来,形成一二级缓存。使用流程大致如下:

去一级缓存中查找数据(caffeine-本地应用内)

如果没有的话,去二级缓存中查找数据(redis-内存)

再没有,再去数据库中查找数据(数据库-磁盘)

🔟Redis中常见的数据结构,应用场景:

🔑:

  • 5 种基础数据结构 :String(字符串)、List(列表)、Set(集合)、Hash(散列)、Zset(有序集合)。

  • 3 种特殊数据结构 :HyperLogLogs(基数统计)、Bitmap (位存储)、Geospatial (地理位置)。

1️⃣1️⃣缓存穿透和缓存雪崩的区别,解决办法:

🔑:区别:缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起为id为“-1”的数据或id为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大。

缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。缓存雪崩是因为不同数据都过期了,很多数据都查不到从而查数据库。

缓存穿透的解决办法:

1.接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;

2.从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击。

缓存雪崩的解决办法:

  1. 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。

  1. 如果缓存数据库是分布式部署,将热点数据均匀分布在不同搞得缓存数据库中。

  1. 设置热点数据永远不过期。

1️⃣2️⃣MySQL和Redis怎么保持数据一致:

🔑:在高并发应用场景下,如果是对数据一致性要求高的情况下,要定位好导致数据和缓存不一致的原因。解决高并发场景下数据一致性的方案有两种,分别是延时双删策略和异步更新缓存两种方案。

也可以采用别的第三方:kafka、rabbitMQ等来实现推送更新Redis!

这篇博客写的很详细:Redis和MySQL如何保持数据一致性_redis和mysql数据一致性怎么保证_☆夜幕星河℡的博客-CSDN博客

1️⃣3️⃣一个SQL笔试题,join多表查询(共享屏幕):这个不写解答了。


总结

因为知识比较匮乏,我只能把答案整理出来给大家分享,同时自己也能有所长进,大家一起加油!

觉得上面有什么问题的可以提出呀,欢迎讨论。

  • 9
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

垫脚摸太阳

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

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

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

打赏作者

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

抵扣说明:

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

余额充值