欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java常见面试题

程序员文章站 2022-04-11 18:15:26
...

1.集合线程不安全解决

故障现象

public class ContainerDemo {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        Random random = new Random();
        for (int i = 0; i < 100; i++) {
            new Thread(() -> {
                list.add(random.nextInt(10));
                System.out.println(list);
            }).start();
        }
    }
}
故障现象java.util.ConcurrentModificationException

导致原因

并发修改导致的异常

解决方案

new Vector();
Collections.synchronizedList(new ArrayList<>());
List list = new CopyOnWriteArrayList<>();

优化建议

读多写少的时候推荐使用Java.util. concurrent.CopeOnWriteArrayList 类

2.Java中的锁synchronized 和 Lock 有什么区别?

1.原始结构

synchronized 是关键字属于 JVM 层面,反应在字节码上是 monitorenter 和 monitorexit,其底层是通过 monitor 对象来完成,其实 wait/notify 等方法也是依赖 monitor 对象只有在同步快或方法中才能调用 wait/notify 等方法。
Lock 是具体类(java.util.concurrent.locks.Lock)是 api 层面的锁。

2.使用方法

synchronized 不需要用户手动去释放锁,当 synchronized 代码执行完后系统会自动让线程释放对锁的占用。
ReentrantLock 则需要用户手动的释放锁,若没有主动释放锁,可能导致出现死锁的现象,lock() 和 unlock() 方法需要配合 try/finally 语句来完成。

3.等待是否可中断

synchronized 不可中断,除非抛出异常或者正常运行完成。
ReentrantLock 可中断,设置超时方法 tryLock(long timeout, TimeUnit unit),lockInterruptibly() 放代码块中,调用 interrupt() 方法可中断.

4.加锁是否公平

synchronized 非公平锁
ReentrantLock 默认非公平锁,构造方法中可以传入 boolean 值,true 为公平锁,false 为非公平锁。

5.锁可以绑定多个 Condition

synchronized 没有 Condition。
ReentrantLock 用来实现分组唤醒需要唤醒的线程们,可以精确唤醒,而不是像 synchronized 要么随机唤醒一个线程要么唤醒全部线程。

3.死锁编码以及定位分析(项目怎么解决异常,遇到的难题是什么)

产生死锁的原因

1.系统资源不足。2.系统资源分配不足
死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种相互等待的现象,如果无外力的干涉那它们都将无法推进下去,如果系统的资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则就会因争夺有限的资源而陷入死锁。

  • jps -l 命令查定位进程号
    Java常见面试题
28519 org.jetbrains.jps.cmdline.Launcher
32376 com.intellij.idea.Main
28521 com.cuzz.thread.DeadLockDemo
27836 org.jetbrains.kotlin.daemon.KotlinCompileDaemon
28591 sun.tools.jps.Jps
  • jstack 28521 找到死锁查看
    Java常见面试题
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.191-b12 mixed mode):

"Attach Listener" #13 daemon prio=9 os_prio=0 tid=0x00007f7acc001000 nid=0x702a waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE
// ...
Found one Java-level deadlock:
=============================
"pool-1-thread-2":
  waiting to lock monitor 0x00007f7ad4006478 (object 0x00000000d71f60b0, a java.lang.String),
  which is held by "pool-1-thread-1"
"pool-1-thread-1":
  waiting to lock monitor 0x00007f7ad4003be8 (object 0x00000000d71f60e8, a java.lang.String),
  which is held by "pool-1-thread-2"

Java stack information for the threads listed above:
===================================================
"pool-1-thread-2":
        at com.cuzz.thread.DeadLockDemo.method(DeadLockDemo.java:34)
        - waiting to lock <0x00000000d71f60b0> (a java.lang.String)
        - locked <0x00000000d71f60e8> (a java.lang.String)
        at com.cuzz.thread.DeadLockDemo.lambda$main$1(DeadLockDemo.java:21)
        at com.cuzz.thread.DeadLockDemo$$Lambda$2/2074407503.run(Unknown Source)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)
"pool-1-thread-1":
        at com.cuzz.thread.DeadLockDemo.method(DeadLockDemo.java:34)
        - waiting to lock <0x00000000d71f60e8> (a java.lang.String)
        - locked <0x00000000d71f60b0> (a java.lang.String)
        at com.cuzz.thread.DeadLockDemo.lambda$main$0(DeadLockDemo.java:20)
        at com.cuzz.thread.DeadLockDemo$$Lambda$1/558638686.run(Unknown Source)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)

Found 1 deadlock.

4.线程池难点

为什使用线程池,线程池的优势?

线程池用于多线程处理中,它可以根据系统的情况,可以有效控制线程执行的数量,优化运行效果。线程池做的工作主要是控制运行的线程的数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了最大数量,那么超出数量的线程排队等候,等其它线程执行完毕,再从队列中取出任务来执行。

主要特点为

1.线程复用。2.控制最大并发数量。3.管理线程

主要优点

1.低资源消耗,通过重复利用已创建的线程来降低线程创建和销毁造成的消耗。
2.提高相应速度,当任务到达时,任务可以不需要的等到线程创建就能立即执行。
3.提高线程的可管理性,线程是稀缺资源,如果无限制的创建,不仅仅会消耗系统资源,还会降低体统的稳定性,使用线程可以进行统一分配,调优和监控。

线程池使用
Java常见面试题
ThreadPoolExecutor最重要的底层源码实现类

ThreadPoolExecutor作为java.util.concurrent包对外提供基础实现,以内部线程池的形式对外提供管理任务执行,线程调度,线程池管理等等服务。

有五种常使用三种

ExecutorService thread1 = Executors.newFixedThreadPool(5);//一个池五个线程
thread1.execute(new Runnable());//开启线程池,没有返回值。可以执行任务,但无法判断任务是否成功完成。
Future future = thread1.submit(new Runnable());//开启线程池,返回一个future。可以用这个future来判断任务是否成功完成
thread1.shutdown();//关闭线程池

ExecutorService thread2 = Executors.newSigleThreadExecutor();//一个池1个处理线程

ExecutorService thread3 = Executors.newCachedThreadPool();//一个池N个处理线程

Java常见面试题
7大参数

参数 说明
corePoolsize 线程池中的常驻核心线程数
maximumpoolsiz 线池能够容纳同时执行的最大线程数,此值必须大于等于1
keepalivetime 多余的空闲线程的存活时间多余空闲线程会被销毁直到只剩下 corepoolSize个线程为止
TimeUnit keepAliveTime 时间单位
workQueue 任务队列,被提交但尚未被执行的任务
threadFactory 表示生成线程池中工作线程的线程工厂,用于创建线程一般用默认的即可
RejectedExecutionHandler 当提交任务数超过 maxmumPoolSize+workQueue 之和时,任务会交给RejectedExecutionHandler 来处理

Java常见面试题
Java常见面试题
原理

1.当线程池小于corePoolSize时,新提交任务将创建一个新线程执行任务,即使此时线程池中存在空闲线程。
2.当线程池达到corePoolSize时,新提交任务将被放入 workQueue 中,等待线程池中任务调度执行。
3.当workQueue已满,且 maximumPoolSize 大于 corePoolSize 时,新提交任务会创建新线程执行任务。
4.当提交任务数超过 maximumPoolSize 时,新提交任务由 RejectedExecutionHandler 处理。
5.当线程池中超过corePoolSize 线程,空闲时间达到 keepAliveTime 时,关闭空闲线程 。
6.当设置allowCoreThreadTimeOut(true) 时,线程池中 corePoolSize 线程空闲时间达到 keepAliveTime 也将关闭。

生产环境使用的线程池手写(不使用JDK里)
Java常见面试题

public class ThreadPoolExecutorDemo {
    public static void main(String[] args) {
        Executor executor = new ThreadPoolExecutor(2, 3, 1L, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(5), 
                Executors.defaultThreadFactory(), 
                new ThreadPoolExecutor.DiscardPolicy());
    }
}

合理配置线程池考虑二点

1.CPU 密集型
CPU 密集的意思是该任务需要大量的运算,而没有阻塞,CPU 一直全速运行。
CPU 密集型任务尽可能的少的线程数量,一般为 CPU 核数 + 1 个线程的线程池。
2.IO 密集型
由于 IO 密集型任务线程并不是一直在执行任务,可以多分配一点线程数,如 CPU * 2 。
也可以使用公式:CPU 核数 / (1 - 阻塞系数);其中阻塞系数在 0.8 ~ 0.9 之间。

5.Linux查性能命令

top 查看机器性能 简写uptime
Java常见面试题
vmstat 主要查看CPU性能
Java常见面试题
free 看内存
Java常见面试题
df 看硬盘
Java常见面试题
ps 查看进程