一、数据库

1. exists 和 in 的区别?
  • in适合于外表大而内表小的情况;exists适合于外表小而内表大的情况;这样效率会高的
2. 聊聊组合索引和最左匹配原则?
  • 什么是组合索引?
    • 两个或更多个列上的索引被称作联合索引,联合索引又叫组合索引
    • 例如索引是 key index(a, b, c) 可以支持 a | a,b | a,b,c 三种组合进行查找,但不支持 b,c 进行查找。在使用的时候,a, c 组合也可以用,但实际上只用到了 a 的 索引

二、JUC

1. Thread中的join()方法?
  • 作用1:新线程加入,所以要等待它执行完再出发
  • 作用2:中断,Thread.join()响应的中断是执行join方法的这个线程的中断,而不是Thread
  • join()期间线程状态:WAITING
2. 线程池的工作流程
  1. 提交任务
    • 当向线程池提交一个新的任务时,线程池有三种处理情况,分别是:创建一个工作线程来执行该任务、将任务加入阻塞队列、拒绝该任务。
    • 提交任务的过程也可以拆分成以下几个部分:
      • 当工作线程数小于核心线程数时,直接创建新的核心工作线程;
      • 当工作线程数不小于核心线程数时,就需要尝试将任务添加到阻塞队列中去;
      • 如果能够加入成功,说明队列还没有满,那么需要做以下的二次验证来保证添加进去的任务能够成功被执行;
      • 验证当前线程池的运行状态,如果是非RUNNING状态,则需要将任务从阻塞队列中移除,然后拒绝该任务;
      • 验证当前线程池中的工作线程的个数,如果为0,则需要主动添加一个空工作线程来执行刚刚添加到阻塞队列中的任务;
      • 如果加入失败,则说明队列已经满了,那么这时就需要创建新的“临时”工作线程来执行任务;
      • 如果创建失败,则直接执行该任务
      • 如果创建失败,则说明工作线程数已经等于最大线程数了,则只能拒绝该任务了
  2. 创建工作线程
    创建工作线程需要做一系列的判断,需要确保当前线程池可以创建新的线程之后,才能创建;
    首先,当线程池的状态是SHUTDOWN或者STOP时,则不能创建新的线程;
    另外,当线程工厂创建线程失败时,也不能创建新的线程;
    还有就是当前工作线程的数量与核心线程数、最大线程数进行比较,如果前者大于后者的话,也不允许创建;
    除此之外,会尝试通过CAS来自增工作线程的个数,如果自增成功了,则会创建新的工作线程,即Worker对象;
    然后加锁进行二次验证是否能够创建工作线程,最后如果创建成功,则会启动该工作线程
  3. 启动工作线程

当工作线程创建成功后,也就是Worker对象已经创建好了,这时就需要启动该工作线程,让线程开始干活了,Worker对象中关联着一个Thread,所以要启动工作线程的话,只要通过worker.thread.start()来启动该线程即可。

启动完了之后,就会执行Worker对象的run()方法,因为Worker实现了Runnable接口,所以本质上Worker也是一个线程。

通过线程start开启之后就会调用到Runnable的run方法,在worker对象的run方法中,调用了runWorker(this)方法,也就是把当前对象传递给了runWorker方法,让他来执行。
4. 获取任务并执行
在 runWorker() 方法被调用之后,就是执行具体的任务了,首先需要拿到一个可以执行的任务,而Worker对象中默认绑定了一个任务,如果该任务不为空的话,那么就是直接执行。

执行完了之后,就会去阻塞队列中获取任务来执行,而获取任务的过程,需要考虑当前工作线程的个数。

  • 如果工作线程数大于核心线程数,那么就需要通过poll来获取,因为这时需要对闲置的线程进行回收;
  • 如果工作线程数小于核心线程数,那么就需要通过take来获取了,因为这时所有的线程都是核心线程,不需要进行回收,前提是没有设置 allowCoreThreadTimeOut

三、算法

  1. java 实现0001…A000…ZA00…ZA99…ZZZZ流水号的自动生成