阿里云内推一面挂

对于本次面试,感觉太紧张,本该会的题目都答得不好,没有开源项目,看过的底层源码也没复习,还是要加大力度搞学习。以下是面试题及相关答案(面试完补充的答案,不知道是否正确,还望大佬指正):
1、索引种类

MYSQL数据库索引类型包括普通索引,唯一索引,主键索引与组合索引

普通索引,这是最基本的MySQL数据库索引,它没有任何限制。

唯一索引,它与前面的普通索引类似,不同的就是:MySQL数据库索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。

为了进一步榨取MySQL的效率,就要考虑建立组合索引,符合“最左前缀”原则


2、聚簇索引

定义:数据库表的索引从数据存储方式上可以分为聚簇索引和非聚簇索引(又叫二级索引)两种。Innodb的聚簇索引在同一个B-Tree中保存了索引列和具体的数据,在聚簇索引中,实际的数据保存在叶子页中,中间的节点页保存指向下一层页面的指针。“聚簇”的意思是数据行被按照一定顺序一个个紧密地排列在一起存储。一个表只能有一个聚簇索引,因为在一个表中数据的存放方式只有一种。

优点:聚簇索引将索引和数据行保存在同一个B-Tree中,查询通过聚簇索引可以直接获取数据,相比非聚簇索引需要第二次查询(非覆盖索引的情况下)效率要高。聚簇索引将索引和数据行保存在同一个B-聚簇索引对于范围查询的效率很高,因为其数据是按照大小排列的。


3、如何查询SQL执行时间

show profiles

查看show profiles功能是否开启:show variables like "%pro%";

开启show profiles功能:set profiling = 1;

查看每条执行过的SQL语句的执行时间:show profiles;

查看一条SQL语句的详细执行时间:show profile for query Query_ID


4、explain执行结果都有什么字段

id:查询序号

select_type:查询类型

table:表名

partitions:匹配的分区

type:join类型

prossible_keys:可能会选择的索引

key:实际选择的索引

key_len:索引的长度

ref:与索引作比较的列

rows:要检索的行数(估算值)

filtered:查询条件过滤的行数的百分比

Extra:额外信息


5、多线程实现方式

答案:继承Thread类创建线程、实现Runnable接口创建线程、实现Callable接口通过FutureTask包装器来创建Thread线程、使用ExecutorService、Callable、Future实现有返回结果的线程


6、HashMap实现原理

答案:

HashMap是一种Key-Value数据结构,线程不安全

以JDK8而言put方法逻辑

1)如果HashMap未被初始化过,则初始化

2)对key求hash值((n - 1) & hash),然后计算下标

3)如果没有产生hash冲突,直接放入桶中

4)如果产生hash碰撞,以链表形式链接到后面

5)如果链表长度超过阈值8,则把链表变成红黑树

6)如果链表长度低于6,就把红黑树转换回链表

7)如果节点已经存在就替换旧值

8)如果桶满了( capacity * 0.75),就需要resize扩容2倍capacity,重新进行hash,存储旧元素和新元素


7、了解其它的编程语言

答案:了解C/C++、Python


8、如何创建线程池

答案:

通过Executors创建不同的线程满足不同的场景

1)newFixedThreadPool(int nThreads)

指定工作数量的线程池

2)newCachedThreadPool

处理短时间工作任务的线程池

试图缓存线程并重用,当无缓存线程可用时,就会创建新的工作线程

如果线程闲置的时间已超过阈值,则会被终止并移出缓存

系统长时间闲置的时候不会消耗资源

3)newSingleThreadExecutor

创建唯一的工作线程来执行任务,如果线程异常结束,会有另外线程取代它

4)newSingleThreadScheduledExecutor()与newScheduledThreadPool newScheduledThreadPool(int corePoolSize)都是进行定时或者周期性任务调度,两者区别在于单一线程还是多个线程

5)newWorkStealingPool(int parallelism)

内部会构建ForkJoinPool,利用working-stealing算法,并并行地处理任务

Fork/Join框架把大任务分割成若干个小任务并行执行,最终汇总每个小任务结果得到大任务结果



9、线程池拒绝策略

答案:当线程池表示拒绝处理任务时有四种策略:AbortPplicy(直接抛出异常,线程池默认拒绝策略)、DiscardPolicy(直接丢弃任务)、DiscardOldestPolicy(丢弃队列中最旧(队头)任务,并执行当前任务)、CallerRunsPolicy(不用线程池中的线程执行,用调用所在线程执行)


10、阻塞队列应用场景

阻塞队列是一个支持附加操作的队列。这两个附加操作是:在队列为空时。获取元素的线程会等待队列为非空;当队列满时,存储元素的线程会等队列可用。阻塞队列常用于生产者和消费者的场景,生成者是往队列里添加元素的线程,消费者是从队列里拿走元素的线程。


11、dubbo负载均衡

答案:

dubbo的负载均衡策略,主体向外暴露出来是一个接口,名字叫做loadBlace,接口下是一个抽象类AbstractLoadBlance,dubbo拥有4种负载均衡策略,分别是一致性Hash均衡算法、随机调用法、轮询法、最少活动调用法,四种算法都继承抽象类AbstractLoadBalance,重写doSelect方法。


ConsistentHashLoadBalance:

一致性Hash算法,doSelect方法进行选择。一致性Hash负载均衡涉及到两个主要的配置参数为hash.arguments与hash.nodes:当进行调用时候根据调用方法的哪几个参数生成key,并根据key来通过一致性hash算法来选择调用节点。例如调用方法invoke(Strings1,Strings2);若hash.arguments为1(默认值),则仅取invoke的参数1(s1)来生成hashCode。


RandomLoadBalance:

随机调用负载均衡,方法的细节就是首先遍历每个提供服务的机器,获取每个服务的权重,然后累加权重值,判断每个服务的提供者权重是否相同,如果每个调用者的权重不相同,并且每个权重大于0,那么就会根据权重的总值生成一个随机数,再用这个随机数,根据调用者的数量每次减去调用者的权重,直到计算出当前的服务提供者随机数小于0,就选择那个提供者!另外,如果每个机器的权重的都相同,那么权重就不会参与计算,直接选择随机算法生成的某一个选择,完全随机。


RoundRobinLoadBlance:

轮询调用,轮询调用的过程主要是维护了局部变量的一个LinkdesHashMap(有顺序的Map)去存储调用者和权重值的对应关系,然后遍历每个调用者,把调用者和当前大于0的权重值放进去,再累加权重值。还有一个全局变量的map,找到第一个服务调用者,首先是找到每个服务的key值和method,这里可以理解为标识第一个调用者的唯一key,然后再给它对应的值保证原子性的+1(AtomicPositiveInteger是原子的),再对这个值取模总权重,再每次对其权重值-1,知道它取模与总权重值等于0就选择该调用者,可以称之为"降权取模"(只是一种的计算层面,而不是真正降权)。总结:轮询调用并不是简单的一个接着一个依次调用,它是根据权重的值进行循环的


LeastActiveLoadBlance:

最少活跃数调用法:这个方法的主要作用根据服务的提供者的运行状态去选择服务器,主要的思路就是遍历每个调用者,然后获取每个服务器的运行状态,如果当前运行的运行状态小于最小的状态-1,把它保存在leastIndexs中的第一个位置,并且认定所有的调用者权重都相同,然后直接返回那个调用者(这里的逻辑是:找到最少活跃数(在代码层反应就是:active的值))。如果计算出的权重值和最少的权重值相同,那么把它保存在leastIndexs数组里面,累加权重值,如果当前的权重值不等于初始值firstWeight,那么就认定不是所有的调用者的权重不同。然后再遍历lestIndexs,取权重累加值的随机数生成权重偏移量,在累减它,到它小于0的时候返回那个调用者。如果这些都不符合,就从leastIndexs随机选一个index,返回那个调用者!


dubbo默认的负载均衡策略是随机调用法(@SPI注解)、@Reference可改变



12、框架或者JDK底层源码


13、阿里云表格存储


14、开源项目


15、生产消费模型(编程题)

答案:

自己使用ArrayBlockingQueue方式


import java.util.Random;

import java.util.concurrent.ArrayBlockingQueue;


public class Main {

public static void main(String[] args) {

ArrayBlockingQueue<Integer> abq = new ArrayBlockingQueue<>(50);

Random random = new Random();

new Thread(()->{

while(true){

try {

Integer msg = abq.take();

System.out.println("Consume : " + msg);

Thread.sleep(500);

}catch (InterruptedException e){

e.printStackTrace();

}

}

}).start();

while (true){

Integer msg = random.nextInt();

System.out.println("Produce : " + msg);

if (abq.offer(msg) == false) {

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}


}

}


使用LinkedBlockingQueue方式:

import java.util.concurrent.BlockingQueue;

import java.util.concurrent.LinkedBlockingQueue;


public class ProducerConsumerPattern {

public static void main(String[] args) {

BlockingQueue bq = new LinkedBlockingQueue();

Thread producer = new Thread(new Producer(bq));

Thread consumer = new Thread(new Consumer(bq));


producer.start();

consumer.start();

}


}


class Producer implements Runnable {

private final BlockingQueue bq;


public Producer(BlockingQueue bq) {

this.bq = bq;

}


@Override

public void run() {

for (int i = 0; i < 10; i++) {

try {

System.out.println("Produced : " + i);

bq.put(i);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}


class Consumer implements Runnable {

private final BlockingQueue bq;


public Consumer(BlockingQueue bq) {

this.bq = bq;

}


@Override

public void run() {

while(true){

try {

Integer msg = (Integer)bq.take();

System.out.println("Consumed : " + msg);

System.out.println();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}


使用Object的wait()和notify()方式:

import java.util.PriorityQueue;

import java.util.Random;


public class ProducerConsumerPattern {

static PriorityQueue<Integer> queue = new PriorityQueue<>(10);

static Random random = new Random();

static class Consumer extends Thread{

@Override

public void run() {

while (true){

synchronized (queue){

while (queue.size() == 0){

try {

queue.wait();

}catch (InterruptedException e){

e.printStackTrace();

queue.notify();

}

}

System.out.println(queue.poll());

queue.notify();

}

}

}

}


static class Producer extends Thread{

@Override

public void run() {

while (true){

synchronized (queue){

while (queue.size() == 10){

try {

queue.wait();

} catch (InterruptedException e) {

e.printStackTrace();

queue.notify();

}

}

queue.offer(random.nextInt(11));

queue.notify();

}

}

}

}


public static void main(String[] args) {

Thread producer = new Thread(new Producer());

Thread consumer = new Thread(new Consumer());

producer.start();

consumer.start();

}


}


使用Condition方式:

import java.util.PriorityQueue;

import java.util.Random;

import java.util.concurrent.locks.Condition;

import java.util.concurrent.locks.Lock;

import java.util.concurrent.locks.ReentrantLock;


public class ProducerConsumerPattern {

private static PriorityQueue<Integer> queue = new PriorityQueue<>(10);

private static Random random = new Random();


private static Lock lock = new ReentrantLock();

private static Condition notFull = lock.newCondition();

private static Condition notEmpty = lock.newCondition();


static class Consumer extends Thread{

@Override

public void run() {

while (true){

lock.lock();

try{

while (queue.size() == 0){

try {

notEmpty.await();

}catch (InterruptedException e){

e.printStackTrace();

}

}

System.out.println(queue.poll());

notFull.signal();

}finally {

lock.unlock();

}

}

}

}


static class Producer extends Thread{

@Override

public void run() {

while (true){

lock.lock();

try{

while (queue.size() == 10){

try {

notFull.await();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

queue.offer(random.nextInt(11));

notEmpty.signal();

}finally {

lock.unlock();

}

}

}

}


public static void main(String[] args) {

Producer producer = new Producer();

Consumer consumer = new Consumer();

new Thread(producer).start();

new Thread(consumer).start();

}


}


#阿里巴巴##面经##校招##Java工程师#
全部评论
加油
点赞 回复 分享
发布于 2019-08-22 17:09

相关推荐

dongsheng66:如果想进大厂的话,在校经历没必要占这么大篇幅,可以把专业技能单独放一个专栏写,可以加个项目经历
点赞 评论 收藏
分享
2 61 评论
分享
牛客网
牛客企业服务