关于java:蔚来真题和答案主打一个简单

38次阅读

共计 6820 个字符,预计需要花费 18 分钟才能阅读完成。

问题起源如下:

<img src=”https://cdn.nlark.com/yuque/0/2023/png/92791/1687946200989-e5852be8-69f0-49a9-9b40-f3960db36c2d.png” width=”50%”>

问题链接:https://www.nowcoder.com/discuss/493178141461041152

答案解析

1. 解释脏读 / 不可反复读 / 幻读

  • 脏读:指一个事务读取到了另一个事务为提交保留的数据,之后此事务进行了回滚操作,从而导致第一个事务读取了一个不存在的脏数据。
  • 不可反复读:在同一个事务中,同一个查问在不同的工夫失去了不同的后果。例如事务在 T1 读取到了某一行数据,在 T2 工夫从新读取这一行时候,这一行的数据曾经产生批改,所以再次读取时失去了一个和 T1 查问时不同的后果。
  • 幻读:同一个查问在不同工夫失去了不同的后果,这就是事务中的幻读问题。例如,一个 SELECT 被执行了两次,然而第二次返回了第一次没有返回的一行,那么这一行就是一个“幻像”行。

    不可反复读和幻读的区别

  • 不可反复读的重点是批改:在同一事务中,同样的条件,第一次读的数据和第二次读的数据不一样。(因为两头有其余事务提交了批改);
  • 幻读的重点在于新增或者删除:在同一事务中,同样的条件,,第一次和第二次读出来的记录数不一样。(因为两头有其余事务提交了插入 / 删除)。

    2. 索引生效的场景有哪些?

    常见的索引生效场景有以下这些:

  1. 未遵循最左匹配准则
  2. 应用列运算
  3. 应用函数办法
  4. 类型转换
  5. 应用 is not null
  6. 谬误的含糊匹配,应用右 % 开始查问。

具体内容请参考:https://www.javacn.site/interview/mysql/indexinvalid.html

3.Explain 执行打算用过吗?

Explain 是用来剖析 SQL 的执行状况的,explain 应用如下,只须要在查问的 SQL 后面增加上 explain 关键字即可,如下图所示:

而以上查问后果的列中,咱们最次要察看 key 这一列,key 这一列示意理论应用的索引,如果为 NULL 则示意未应用索引,反之则应用了索引。

以上所有后果列阐明如下:

  • id — 抉择标识符,id 越大优先级越高,越先被执行;
  • select_type — 示意查问的类型;
  • table — 输入后果集的表;
  • partitions — 匹配的分区;
  • type — 示意表的查问类型;
  • possible_keys — 示意查问时,可能应用的索引;
  • key — 示意理论应用的索引;
  • key_len — 索引字段的长度;
  • ref— 列与索引的比拟;
  • rows — 大略估算的行数;
  • filtered — 按表条件过滤的行百分比;
  • Extra — 执行状况的形容和阐明。

4.Type 字段有什么信息?

Explain 执行打算中最重要的就是 type 字段,type 蕴含的信息如下:

  • all — 扫描全表数据;
  • index — 遍历索引;
  • range — 索引范畴查找;
  • index_subquery — 在子查问中应用 ref;
  • unique_subquery — 在子查问中应用 eq_ref;
  • ref_or_null — 对 null 进行索引的优化的 ref;
  • fulltext — 应用全文索引;
  • ref — 应用非惟一索引查找数据;
  • eq_ref — 在 join 查问中应用主键或惟一索引关联;
  • const — 将一个主键搁置到 where 前面作为条件查问,MySQL 优化器就能把这次查问优化转化为一个常量,如何转化以及何时转化,这个取决于优化器,这个比 eq_ref 效率高一点。

5.binlog 和 redolog 区别?

binlog(二进制日志)和 redolog(重做日志)都是 MySQL 中的重要日志,但二者存在以下不同。

  1. binlog(二进制日志):

    • binlog 是 MySQL 的服务器层日志,用于记录对数据库执行的所有批改操作,包含插入、更新和删除等。它以二进制格局记录,能够被用于数据复制、复原和故障复原等操作。
    • binlog 记录了逻辑上的操作,即执行的 SQL 语句或语句的逻辑示意。
    • binlog 是在事务提交后才会生成,因而它是长久化的。
    • binlog 能够被配置为不同的格局,包含基于语句的复制(statement-based replication)、基于行的复制(row-based replication)和混合复制(mixed replication)。
  2. redolog(重做日志):

    • redolog 是 MySQL 的存储引擎层日志,用于确保数据库的事务持久性和解体恢复能力。
    • redolog 记录了物理层面的批改操作,即对数据页的物理批改。它次要用于保障事务的持久性,确保在产生解体时,曾经提交的事务对数据库的批改可能被复原。
    • redolog 是循环写入的,它的数据写入到磁盘上的文件中。在产生解体时,通过 redolog 的重做操作,能够将数据库复原到解体前的统一状态。
    • redolog 是在事务执行期间一直写入的,以确保在零碎解体时能够重做所有已提交的事务。

小结:binlog 用于记录逻辑层面的操作,能够用于数据复制和复原,而 redolog 用于记录物理层面的操作,确保事务的持久性和解体复原。它们在性能和应用上有一些不同,但都是 MySQL 中重要的日志机制。

6.Redis 根本数据类型

Redis 罕用的数据类型有 5 种:String 字符串类型、List 列表类型、Hash 哈希表类型、Set 汇合类型、Sorted Set 有序汇合类型,如下图所示:这 5 种罕用类型的用处如下:

  1. String:字符串类型,常见应用场景是:存储 Session 信息、存储缓存信息(如详情页的缓存)、存储整数信息,可应用 incr 实现整数 +1,和应用 decr 实现整数 -1;
  2. List:列表类型,常见应用场景是:实现简略的音讯队列、存储某项列表数据;
  3. Hash:哈希表类型,常见应用场景是:存储 Session 信息、存储商品的购物车,购物车非常适合用哈希字典示意,应用人员惟一编号作为字典的 key,value 值能够存储商品的 id 和数量等信息、存储详情页信息;
  4. Set:汇合类型,是一个无序并惟一的键值汇合,它的常见应用场景是:关注性能,比方关注我的人和我关注的人,应用汇合存储,能够保障人员不会反复;
  5. Sorted Set:有序汇合类型,相比于 Set 汇合类型多了一个排序属性 score(分值),它的常见应用场景是:能够用来存储排名信息、关注列表性能,这样就能够依据关注实现排序展现了。

更多内容请参考:https://www.javacn.site/interview/redis/types.html

7. 有序汇合底层实现数据结构?

有序汇合是由 ziplist (压缩列表) 或 skiplist (跳跃表) 组成的。

  1. 压缩列表 ziplist 实质上就是一个字节数组,是 Redis 为了节约内存而设计的一种线性数据结构,能够蕴含多个元素,每个元素能够是一个字节数组或一个整数。
  2. 跳跃表 skiplist 是一种有序数据结构,它通过在每个节点中维持多个指向其余节点的指针,从而达到快速访问节点的目标。跳跃表反对均匀 O(logN)、最坏 O(N) 复杂度的节点查找,还能够通过程序性操作来批量解决节点。

当数据比拟少时,有 序汇合是压缩列表 ziplist 存储的(反之则为跳跃表 skiplist 存储),应用压缩列表存储必满足以下两个条件:

  1. 有序汇合保留的元素个数要小于 128 个;
  2. 有序汇合保留的所有元素成员的长度都必须小于 64 字节。

如果不能满足以上两个条件中的任意一个,有序汇合将会应用跳跃表 skiplist 构造进行存储。

8. 跳表插入数据的过程?

在开始讲跳跃表的增加流程之前,必须先搞懂一个概念:节点的随机层数。
所谓的随机层数指的是每次增加节点之前,会学生成以后节点的随机层数,依据生成的随机层数来决定将以后节点存在几层链表中。

为什么要这样设计呢?

这样设计的目标是为了保障 Redis 的执行效率。

为什么要生成随机层数,而不是制订一个固定的规定,比方下层节点是上层逾越两个节点的链表组成,如下图所示:

如果制订了规定,那么就须要在增加或删除时,为了满足其规定,做额定的解决,比方增加了一个新节点,如下图所示:

这样就不满足制订的下层节点逾越上层两个节点的规定了,就须要额定的调整下层中的所有节点,这样程序的效率就升高了,所以应用随机层数,不强制制订规定,这样就不须要进行额定的操作,从而也就不会占用服务执行的工夫了。

增加流程

Redis 中跳跃表的增加流程如下图所示:

  1. 第一个元素增加到最底层的有序链表中(最底层存储了所有元素数据)。
  2. 第二个元素生成的随机层数是 2,所以再减少 1 层,并将此元素存储在第 1 层和最低层。
  3. 第三个元素生成的随机层数是 4,所以再减少 2 层,整个跳跃表变成了 4 层,将此元素保留到所有层中。
  4. 第四个元素生成的随机层数是 1,所以把它按程序保留到最初一层中即可。

其余新增节点以此类推。
更多内容请参考:https://www.javacn.site/company/redis_skiplist.html

9. 线程池有哪些参数?

线程池(ThreadPoolExecutor)有 7 个参数,这 7 个参数的含意如下:

  • 第 1 个参数:corePoolSize 示意线程池的常驻外围线程数。如果设置为 0,则示意在没有任何工作时,销毁线程池;如果大于 0,即便没有工作时也会保障线程池的线程数量等于此值。但须要留神,此值如果设置的比拟小,则会频繁的创立和销毁线程(创立和销毁的起因会在本课时的下半局部讲到);如果设置的比拟大,则会节约系统资源,所以开发者须要依据本人的理论业务来调整此值;
  • 第 2 个参数:maximumPoolSize 示意线程池在工作最多时,最大能够创立的线程数。官网规定此值必须大于 0,也必须大于等于 corePoolSize,此值只有在工作比拟多,且不能寄存在工作队列时,才会用到;
  • 第 3 个参数:keepAliveTime 示意线程的存活工夫,当线程池闲暇时并且超过了此工夫,多余的线程就会销毁,直到线程池中的线程数量销毁的等于 corePoolSize 为止,如果 maximumPoolSize 等于 corePoolSize,那么线程池在闲暇的时候也不会销毁任何线程;
  • 第 4 个参数:unit 示意存活工夫的单位,它是配合 keepAliveTime 参数独特应用的;
  • 第 5 个参数:workQueue 示意线程池执行的工作队列,当线程池的所有线程都在解决工作时,如果来了新工作就会缓存到此工作队列中排队期待执行;
  • 第 6 个参数:threadFactory 示意线程的创立工厂,此参数个别用的比拟少,咱们通常在创立线程池时不指定此参数,它会应用默认的线程创立工厂的办法来创立线程;
  • 第 7 个参数:RejectedExecutionHandler 示意指定线程池的回绝策略,当线程池的工作曾经在缓存队列 workQueue 中存储满了之后,并且不能创立新的线程来执行此工作时,就会用到此回绝策略,它属于一种限流爱护的机制。

更多内容请参考:https://juejin.cn/post/7072921565079273480

10. 回绝策略有哪些?

线程池的回绝策略默认有以下 4 种:

  1. AbortPolicy:停止策略,线程池会抛出异样并中止执行此工作;
  2. CallerRunsPolicy:把工作交给增加此工作的(main)线程来执行;
  3. DiscardPolicy:疏忽此工作,疏忽最新的一个工作;
  4. DiscardOldestPolicy:疏忽最早的工作,最先退出队列的工作。

默认的回绝策略为 AbortPolicy 停止策略。当然除了 JDK 内置的 4 种回绝策略之外,用户还能够自定义回绝策略,通过实现 new RejectedExecutionHandler,并重写 rejectedExecution 办法来实现自定义回绝策略,实现代码如下:

public static void main(String[] args) {
    // 工作的具体方法
    Runnable runnable = new Runnable() {
        @Override
        public void run() {System.out.println("当前任务被执行, 执行工夫:" + new Date() +
                               "执行线程:" + Thread.currentThread().getName());
            try {
                // 期待 1s
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {e.printStackTrace();
            }
        }
    };
    // 创立线程, 线程的工作队列的长度为 1
    ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, 1,
                                                           100, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1),
                                                           new RejectedExecutionHandler() {
                                                               @Override
                                                               public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                                                                   // 执行自定义回绝策略的相干操作
                                                                   System.out.println("我是自定义回绝策略~");
                                                               }
                                                           });
    // 增加并执行 4 个工作
    threadPool.execute(runnable);
    threadPool.execute(runnable);
    threadPool.execute(runnable);
    threadPool.execute(runnable);
}

11. 你罕用的回绝策略是哪种?为什么?

最罕用的回绝策略是自定义回绝策略,因为外面能够实现本人的业务代码,比方,咱们能够通过自定义回绝策略,发送正告信息给相干人员,这样就能及时发现程序执行的问题,同时再将回绝的工作记录下来,让开发人员手动解决,这样就能够及时发现问题,并解决问题了。

12. 三个线程交替打印 ABC

三个线程交替打印 ABC 的实现办法有很多,我集体比拟偏向于应用 JUC 下的 CyclicBarrier(循环栅栏,也叫循环屏障)来实现,因为循环栅栏天生就是用来实现一轮一轮多线程工作的,它的实现代码如下:

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

/**
 * 3 个线程交替打印 ABC
 */
public class ThreadLoopPrint {
    // 共享计数器
    private static int sharedCounter = 0;
    public static void main(String[] args) {
        // 打印的内容
        String printString = "ABC";
        // 定义循环栅栏
        CyclicBarrier cyclicBarrier = new CyclicBarrier(3, () -> {});
        // 执行工作
        Runnable runnable = new Runnable() {
            @Override
            public void run() {for (int i = 0; i < printString.length(); i++) {synchronized (this) {
                        sharedCounter = sharedCounter > 2 ? 0 : sharedCounter; // 循环打印
                        System.out.println(printString.toCharArray()[sharedCounter++]);
                    }
                    try {
                        // 期待 3 个线程都打印一遍之后,持续走下一轮的打印
                        cyclicBarrier.await();} catch (InterruptedException e) {e.printStackTrace();
                    } catch (BrokenBarrierException e) {e.printStackTrace();
                    }
                }
            }
        };
        // 开启多个线程
        new Thread(runnable).start();
        new Thread(runnable).start();
        new Thread(runnable).start();}
}

以上程序执行的后果如下图所示:

更多内容请参考:https://www.javacn.site/interview/code/weilai_thread.html

13. 力扣括号生成

参考官网解题思路和实现代码:https://leetcode.cn/problems/generate-parentheses/solution/gua-hao-sheng-cheng-by-leetcode-solution/

参考 & 鸣谢

我没有三颗心脏

本文已收录到我的面试小站 www.javacn.site,其中蕴含的内容有:Redis、JVM、并发、并发、MySQL、Spring、Spring MVC、Spring Boot、Spring Cloud、MyBatis、设计模式、音讯队列等模块。

正文完
 0