面试记录、问题

2022-11-25-1(三方支付)

1、i++是否是线程安全11

如果是全局变量,则 i++ 不是线程安全的。因为java在处理 i++ 的时候是分步操作的,编译后相当于 i= i+1。这样的话如果第一条线程执行进来,但是还没有执行到 i++ 的时候,第二条线程进来了,这个时候 i 的值还是原来的值。所以是线程不安全的。

解决方案:使用 synchronization 或者使用 AtomicInteger.

2、线程不安全会有什么问题

会造成数据紊乱,导致业务数据错误等。例如抢票,如果线程不安全则会发生票数错误,多个人抢了票,但是剩余票数只减1,这种问题。

3、线程池创建方式及主要参数

线程池的创建主要有两种:

  1. 通过Executors创建,分别可以创建:

    • FixedThreadPool(5) 固定大小的线程池,可以指定并发数量。超过则在队列中等待。
    • CachedThreadPool() 可缓存线程池,若线程数超过处理所需,则会在缓存一段时间后被回收。如果线程不够则会创建新的线程。
    • SingleThreadPool() 创建单个线程的线程池,保证了先进先出的顺序。
    • ScheduledThreadPool() 可以执行延时任务的线程池
    • SingleThreadScheduledExecutor() 创建一个单线程的可执行延时任务的线程池
    • WorkStealingPool() 创建一个抢占式执行的线程池(任务执行顺序不确定)
  2. 通过 ThreadPoolExecutor 创建,也是阿里规范中推荐使用的方式,因为他可以明确线程池的规则,避免了资源耗尽等问题

    主要参数:

    • corePoolSize:核心线程数,一直保持存活的线程数。当线程数小于核心线程数的时候,即使有空闲线程,来了新任务也会创建新的线程处理。

    • maxPoolSize:最大线程数。当线程数 >= 核心线程数,且队列已满,则会创建新的线程处理。当线程数 = 最大线程数的时候,则会拒绝任务并抛出异常,这个可以根据拒绝策略决定处理方式。

    • keepAliveTime:线程空闲时间,当线程的空闲时间达到设定值的时候会退出,直到线程数量 = 核心线程数为止。如果设置了allowCoreThreadTimeout = true,则核心线程也会超时退出。

    • TimeUnit:时间单位

    • BlockQueue:任务队列,可指定容量,当核心线程数达到最大的是后,再来新的任务则会放在这个队列中等待

    • ThreadFactory:线程工厂

    • reJectedExecutionHandler:任务拒绝处理器。

      两种情况会拒绝处理任务:

      • 当线程数达到最大线程数,且队列已满,怎会拒绝新任务
      • 当线程池的 shutdown() 被调用的时候,会等待队列中的任务执行完毕,再shutdown。如果在调用 shutdown 和线程池真正shutdwon之间进来新任务,则会拒绝。

      ThreadPoolExecutor有几个自己的内部实现来处理:

      • AbortPolicy:默认值,丢弃任务,抛运行时异常
      • CallerRunsPolicy:创建新线程执行任务
      • DiscardPolicy:忽视,什么都不管
      • DiscarOldestPolicy:从队列中踢出最先进入(最后一个执行)的任务
      • 还可以实现ReJectedExecutionHandler来自定义拒绝策略。

4、如何解决sql慢查询

  1. 数据库创建是否适合,例如:根据表涉及的业务选择不同的存储引擎,数据量的大小考虑是否需要分库分表
  2. 主键是否自增类型,否则可能会引发页面分裂和记录迁移
  3. 是否过多的表关联查询,根据阿里规范来说是建议不要超过三张表的关联查询
  4. 创建索引,这里并不是说创建的索引越多越好,也要考虑到一下几种情况
    • 表涉及的业务是否会频繁发生变化,毕竟数据修改是要涉及到构建索引的
    • 创建的索引是否命中
    • 尽量使用联合索引,要考虑最左匹配原则
    • 索引失效
      • 最左匹配原则为满足
      • 计算、函数、类型转换都会导致索引失效
      • 不等于也会导致索引失效
      • is null 可以使用索引,但是is not null 就无法使用索引
      • like 通配符以左模糊查询会导致索引失效
      • or 关键字前后只要存咋非索引的列都会导致索引失效
  5. 考虑全表扫描问题,如果只是查询一条数据的话就可以加上 limit 1 使查询到就立即返回,不进行全表扫描
  6. 使用代码来进行处理,必要的情况下可以多次查询,之后在处理数据

5、什么情况下会发生锁表问题

锁表主要发生在 insert、update、delete中

  1. A程序执行了对tabbleA的insert,还没有commite时,B程序也对tableA进行了insert,这样就会发生锁表的问题
  2. 锁表常发生在并发的情况而不是并行,并行时,一个线程操作数据库,另一个线程是不能操作数据库的,这里是cpu的 i/o 分配原则

怎么减少锁表的发生概率:

减少insert、update、delete语句的执行到commite之间的时间,具体说就是批量执行尽量改成单个执行

6、频繁FullGC会产生什么问题,怎么引起的,怎么解决

1.