zoukankan      html  css  js  c++  java
  • 滴滴出行(小桔科技)亲身面试经验分享,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.你还有什么问题?


    后记

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

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

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

    希望有戏吧……

  • 相关阅读:
    Zookeeper全解析——Paxos作为灵魂(转)
    你真的会开发测试框架?
    使用Hypothesis生成测试数据
    poium测试库之JavaScript API封装原理
    PHP接口自动化测试框架实现
    Web项目如何做单元测试
    如何在Appium中使用AI定位
    我写了个项目,帮你学习HTTP接口测试!
    性能测试浅谈
    Web测试框架SeleniumBase
  • 原文地址:https://www.cnblogs.com/codeToSuccess/p/14037234.html
Copyright © 2011-2022 走看看