滴滴出行(小桔科技)亲身面试经验分享,java开发岗

2020.11.25

概要

本次面试是最近刚面的。

PS:本人java开发2年经验,这次面的是滴滴出行(小桔科技)java开发岗。

 

2020.11.30

滴滴又打来电话了, 预定12.3.星期四面试,不过是另一个java岗位,还是一面;

说明这次的凉了,然后又被HR捞起来了……

很迷,我都不知道该说什么好……

等面完后再总结一篇新的文章吧……


 

 

过程

1.2020年11月16日,本人投给滴滴的简历变成了"被查看"状态(也是在拉勾APP投的),不过多会投的就忘了,应该也是前一二天吧。

PS:投给字节跳动的简历还是"投递成功"状态,甚至没有被查看,看来字节跳动是真的不在拉勾上更新简历进度,不过个人觉得这个也不是很重要,只是记录一下。

2.2020年11月23日,本人接到了滴滴的电话,预约面试时间,本人预约了11月24日20:30的面试。

3.然后收到了邮件,其中写着面试时间,以及面试方式;这次要使用腾讯会议PC版视频面试。

4.2020年11月24日,18:00,HR又打来电话,询问面试时间是否有调整,很周到;本人回复不用调整。

5.2020年11月24日,20:30-21:15,进行了滴滴视频面试。

6.今天25日,等待结果中,希望无论如何给一个结果通知……

 


 

 

面试内容

1.自我介绍。

*期间面试官自言自语说本人工作时间不长,本人目前2年java开发经验,如果还不够的话,难道是必须要3-5年?

2.询问做过的项目,主要问项目问的比较多,以及项目细节。

3.你的项目业务比较复杂吗?

答:是的。然后介绍了一个大批量推送的需求是如何实现的。

4.接第三问:你刚才介绍的是技术实现复杂,不是业务复杂。

答:又扩展介绍了一下项目流程,不过面试官认为又回归到介绍技术实现复杂了;只好回答,那它可能并没有那么复杂。

5.编写一个程序,有三个线程,分别输出A/B/C,现在让它们按顺序输出ABC,并循环十次,你能想到几种实现思路?

答:使用公平锁Reentrantlock(true)实现;有一个java线程池也可以实现;使用线程的wait()与notify()也可以实现。(synchronized个人感觉不能实现,这个是非公平锁,不讲顺序)

6.编码实现第五题(点击腾讯会议的共享屏幕),参考答案如下(终于碰到一个可以做出来的编程题了):

首先创建一个自定义线程类,准备使用Reentrantlock:

import java.util.concurrent.locks.ReentrantLock;

public class MyThread extends Thread{

    private ReentrantLock lock;

    private String str;
    public MyThread(String str, ReentrantLock lock){
        this.str = str;
        this.lock = lock;
    }
    @Override
    public void run() {
        for(int i = 0; i<10; i++){
            lock.lock();
            System.out.println(str);
            lock.unlock();
        }
    }
}

 

然后是main方法:

import java.util.concurrent.locks.ReentrantLock;

public class Test {
    public static void main(String[] args) {
        //有三个线程,分别输出ABC,现在要求线程按顺序输出并且循环10次
        ReentrantLock lock = new ReentrantLock(true);
        MyThread t1 = new MyThread("A",lock);
        MyThread t2 = new MyThread("B",lock);
        MyThread t3 = new MyThread("C",lock);

        t1.start();
        t2.start();
        t3.start();
    }
}

 

这样就实现了题目要求。

 

*之后明显感觉难度开始上升。

 

7.你知道volatile关键字解析吗?(从来没听过)

百度:

https://blog.51cto.com/12222886/1964228
https://www.cnblogs.com/dolphin0520/p/3920373.html

●Java中的volatile

    在Java程序中,如果一个变量被volatile关键字修饰,那么这个变量就具有了有序性和可见性。

    有序性:java语言中提供了synchronized和volatile两个关键字保证线程之间操作的有序性,也就是他可以使CPU指令有序。

    可见性:当一个线程操作一个被volatile修饰的变量时,这个变量的修改对其他所有线程都是可见的,因为此时的操作不会将该变量读到当前线程的CPU缓存中进行操作,而是直接操作内存
●个人理解与总结

volatile修饰变量后,这个变量会存入内存,变成共享变量,线程读写时直接操作内存中的这个变量,跳过CPU cache这一步,因此在读取这个变量时总会返回最新写入的值;
并且,在操作这个变量时是有序的,CPU指令有序;
并且,在访问volatile变量时不会执行加锁操作,也就不会使执行线程阻塞,因此volatile变量是一种比synchronized关键字更轻量级的同步机制。

●synchronized原理

synchronized可以修饰方法、对象、类;在修饰方法时又分为实例方法、静态方法、代码块。
对于同步块的实现使用了monitorenter和monitorexit指令:他们隐式的执行了Lock和UnLock操作,用于提供原子性保证。
monitorenter指令插入到同步代码块开始的位置、monitorexit指令插入到同步代码块结束位置,jvm需要保证每个monitorenter都有一个monitorexit对应。
这两个指令,本质上都是对一个对象的监视器(monitor)进行获取,这个过程是排他的,也就是说同一时刻只能有一个线程获取到由synchronized所保护对象的监视器。
线程执行到monitorenter指令时,会尝试获取对象所对应的monitor所有权,也就是尝试获取对象的锁;而执行monitorexit,就是释放monitor的所有权。
详情见:https://www.cnblogs.com/wuzhenzhao/p/10250801.html

 

 

8.数据库有两个条件分别加了索引,按照两个条件查询时索引怎么走(两个索引同时用吗?不知道)

百度与个人总结(MySql):

(1)首先,索引可以给一个字段加,也可以给多个字段加,如图(Navicat):

 

 

 其中,名是自己起的,栏位对应表中的字段;

(2)加索引之后,当select对应字段、或group by、或order by、或其它增删改查时,都可能会用到索引,提高sql执行效率。

如果经常用到多个字段(例如group by或order by多个字段),就应该给多个字段加一条索引。

关于order by,只有出现在where条件中,才可能会走索引;group by等也类似。详情见:https://www.cnblogs.com/zhaoyl/archive/2012/05/04/2483513.html

(3)如果两个条件都加了索引,sql执行时,会选择影响行数较少的索引,即区分度大的索引。(使用explain分析结果时,rows的值小的。)详情见:https://blog.csdn.net/qq_22771739/article/details/85853620

 

9.如何知道一句sql走了哪个索引?(好像是有一个语法是调试sql用的,然而忘了,还是不常用)

百度:参考网址:https://www.cnblogs.com/wqbin/p/12124621.html

使用explain,可以查看sql是否走了索引

explain select * from test group by user

之后,可以查看结果(本人用Navicat执行的sql)。

举个例子:

(1)数据库表

(2)表设计

 

 

(3)索引(自己设置的)

 

 

 (4)执行【explain select * from test group by user】,返回结果:

 

 

 其中,type为index,说明有索引;(type结果值从好到坏依次是:system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL)

key为实际使用的索引,user_index,说明使用了这个字段的索引。

possible_keys为可能需要使用的索引,正常情况下与key相同;这里为空,说明可能用不到索引也能高效完成查询(不过后来发现还是用索引好)。详细原因可见:https://blog.csdn.net/eden_Liang/article/details/108026148

(5)执行【explain select * from test 】返回结果:

 

 

 可以看到,type为ALL,并且key为空,说明没有使用索引。

 

 

 

 

10.线程池实现原理(只基本会用,没研究过原理)

百度:

为什么需要使用线程池?

●当使用大量线程时,减少大量的new线程与GC回收线程的额外开销。

首先,java有四种线程池:

●newSingleThreadExecutor 

创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

●newFixedThreadPool 

创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

●newScheduledThreadPool 

创建一个可定期或者延时执行任务的定长线程池,支持定时及周期性任务执行。

●newCachedThreadPoo 

创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。 

 

原理:

所谓线程池本质是一个hashSet。多余的任务会放在阻塞队列中。

只有当阻塞队列满了后,才会触发非核心线程的创建。所以非核心线程只是临时过来打杂的。直到空闲了,然后自己关闭了。

线程池提供了两个钩子(beforeExecute,afterExecute)给我们,我们继承线程池,在执行任务前后做一些事情。

线程池原理关键技术:锁(lock,cas)、阻塞队列、hashSet(资源池)

详情网址:

https://www.cnblogs.com/rinack/p/9888717.html

https://www.cnblogs.com/franson-2016/p/13291591.html

 

 

11.线程interrupt()作用(已经被问蒙了,这个也不会了)

百度:

interrupt() 方法只是改变中断状态而已,它不会中断一个正在运行的线程。
*相当于只是改变了isIntrerrupt()返回的boolean值。(当然还有些其它操作,不过主要是改变这个值,其它方法中会根据这个方法判断当前线程状态。)
如果线程阻塞前调用这个方法,那么当该线程遇到阻塞时,会抛异常,停止运行;
如果线程处于阻塞状态,调用这个方法,也能让线程抛异常,停止运行。
更确切的说,如果线程被Object.wait, Thread.join和Thread.sleep三种方法之一阻塞,此时调用该线程的interrupt()方法,那么该线程将抛出一个 InterruptedException中断异常(该线程必须事先预备好处理此异常),从而提早地终结被阻塞状态。
如果线程没有被阻塞,这时调用 interrupt()将不起作用,直到执行到wait(),sleep(),join()时,才马上会抛出 InterruptedException。
详情见:https://blog.csdn.net/liujian8654562/article/details/79875853

 

再总结下线程基本方法:

●线程有五种状态,新建,就绪,运行,阻塞,死亡
●new Thread()创建的线程,是新建状态的。
●使用start()方法,线程进入就绪状态;等待获得资源后,执行run()方法中的内容,此时算运行状态。
●线程运行状态中,遇到某些情况时会进入阻塞状态,如需要执行输入输出但是资源不足;或者人为使用sleep(),suspend(),wait()方法,也会让线程处于阻塞状态。
●线程调用stop(),destory(),或者run()方法执行完毕,就会进入死亡状态。
●sleep()方法可以让线程等待一段时间后再运行,此时是阻塞状态,不释放资源;此时会让出cpu,但是不释放锁。
●wait()方法可以让线程进入阻塞状态,释放资源;JVM把这个线程放入等待池;需要等待其它线程使用notify()或notifyAll()
●wait(long timeout)方法可以让线程进入阻塞状态,释放资源;需要等待其它线程使用notify()或notifyAll(),或者超过时间后,这个线程变为就绪状态
●notify()方法可以让线程从阻塞状态变为就绪状态,JVM把这个线程从等待池中取出;一般是其它线程调用这个方法。
●suspend()使线程阻塞,resume()唤醒线程,这两个方法及其它所有方法在线程阻塞时都不会释放占用的锁(如果占用了的话);而wait()和notify()这一对方法会释放锁。

●yield()的作用是让步,它能够让当前线程从“运行状态”进入到“就绪状态”,从而让其他等待线程获取执行权,但是不能保证在当前线程调用yield()之后,其他线程就一定能获得执行权,也有可能是当前线程又回到“运行状态”继续运行。
详情见:https://blog.csdn.net/wordwarwordwar/article/details/85924858


●需要注意,sleep(),suspend(),resume(),yield(),start(),stop(),destory()这些方法的主体是线程,如new Thread().suspend();

●而wait(),notify(),notifyAll()的主体可以是任何对象,例如new String().wait();具体使用方式见下方网址:
关于notify()与notifyAll():https://blog.csdn.net/qq_42547338/article/details/107448668


●还需要注意,java中,destory()方法并没有被实现,例如new Thread().destory(),调用该方法只会抛出一个异常:NoSuchMethodError();这个方法需要自己继承Thread类后重写。
●还需要注意,直接使用stop(),会立即停止线程,可能导致文件流、数据库连接没有关闭,因此不推荐使用。推荐标志位与interrupt()等组合使用来停止线程,详细使用如下(记住只用interrupt()是不会真的停止线程的):
https://www.cnblogs.com/liyutian/p/10196044.html

 

 

 

12.你还有什么问题?

 


 

 

后记

这些问题的答案本人会继续完善。

整体感觉,前半部分还可以,后半部分就都不会了,感觉明显从编程题后难度提升了。

好不容易遇到一个会做的编程题,这次不想又不明不白的凉了。

希望有戏吧……

 

posted @ 2020-11-25 17:14  codeToSuccess  阅读(871)  评论(0编辑  收藏  举报