工作面试题

1.类加载机制

点击跳转资料
双亲委派模型的作用:
(1)涉及到沙箱安全机制,假如开发者自己写了一个和JDK同名(类路径和类名一模一样)的类如java.lang.Integer.java,一旦类似这种同名类被加载问题就出现了,加载器不知道加载哪一个Integer类,有了双亲委派机制就能保证加载的是JDK自己的Integer,这也是JDK设计者为了防止核心类库随意或者恶意篡改。
(2)双亲委派避免了一个类被重复加载,父加载器加载过的目标类不会被子加载器再加载一次,保证了类加载的唯一性。

JVM类加载过程:
加载,验证,准备,解析,初始化,使用,卸载

  • 加载:将.class文件(并不一定是.class。可以是ZIP包,网络中获取)中的二进制字节流读入到JVM中。
    在加载阶段
  • 验证:文件格式,数据是否符合java规范,字节码,符号引用验证
  • 准备:为静态变量在方法区分配内存,并设置默认初始值
  • 解析:虚拟机将常量池内的符号引用替换为直接引用
  • 初始化:根据程序中的赋值语句主动为类变量赋值(当有父类且父类为初始化的时候,先去初始化父类)

2.JVM调优参数

  • Young GC:一般在新生代的Eden区域满了之后就会触发,采用复制算法来回收新生代的垃圾。
  • major GC:也叫full gc当老年代空间不够了,没法放入更多对象了,这个时候就必须执行major GC对老年代进行垃圾回收。system.gc()会增加full gc发生的频率

-XX:+PrintGC: 打印GC信息
-XX:NewSize: 设置年轻代大小
-Xms:初始堆内存大小

-Xmx:堆内存最大值

ps:在抛出OOM之前,老年代或永久代必然经过一次GC回收;OOM在永久代或老年代中抛出
老年代引起OOM的原因:老年代空间只有在新生代对象转入及创建为大对象(需要大量连续内存空间的java对象,例如很长的数组,此种对象会直接进入老年代)大数组时才会出现不足的现象,当执行Full GC后空间仍然不足,则抛出如下错误:java.lang.OutOfMemoryError: Java heap space
永久代引起OOM的原因:当系统中要加载的类、反射的类和调用的方法较多时,Permanet Generation可能会被占满,在未配置为采用CMS GC的情况下也会执行Full GC。如果经过Full GC仍然回收不了,那么JVM会抛出如下错误信息:java.lang.OutOfMemoryError: PermGen space

3.垃圾回收机制

垃圾回收的4种算法:标记-清除、复制算法、标记-整理算法、分代收集

不同垃圾回收器的优缺点
7种垃圾收集器介绍

在这里插入图片描述

4.内存泄漏和内存溢出

区别与解决方式

6.线程池的四种实现方式

四种实现方式区别

Executors中创建线程池的快捷方法,实际上是调用了ThreadPoolExecutor的构造方法(定时任务使用的是ScheduledThreadPoolExecutor),该类构造方法参数列表如下:

// Java线程池的完整构造函数
public ThreadPoolExecutor(
  int corePoolSize, // 线程池长期维持的线程数,即使线程处于Idle状态,也不会回收。
  int maximumPoolSize, // 线程数的上限
  long keepAliveTime, TimeUnit unit, // 超过这个时间,多余的线程会被回收。
  BlockingQueue<Runnable> workQueue, // 任务的排队队列
  ThreadFactory threadFactory, // 新线程的产生方式
  RejectedExecutionHandler handler) // 拒绝策略

workQueue队列类型

  • SynchronousQueue(同步移交队列):队列不作为任务的缓冲方式,可以简单理解为队列长度为零
  • LinkedBlockingQueue(无界队列):队列长度不受限制,当请求越来越多时(任务处理速度跟不上任务提交速度造成请求堆积)可能导致内存占用过多或OOM
  • ArrayBlockintQueue(有界队列):队列长度受限,当队列满了就需要创建多余的线程来执行任务

拒绝策略

  • AbortPolicy:中断抛出异常,它将抛出RejectedExecutionException
  • DiscardPolicy:默默丢弃任务,不进行任何通知
  • DiscardOldestPolicy:它放弃最旧的未处理请求,然后重试execute
  • CallerRunsPolicy:直接在execute方法的调用线程中运行被拒绝的任务(对比前三种比较友好一丢丢)

ThreadPoolTaskExecutor和ThreadPoolExecutor区别:
ThreadPoolTaskExecutor和ThreadPoolExecutor区别

7.常用的线程池有哪几种,用到了什么队列

  • newCachedThreadPool:SynchronousQueue(同步队列)
  • newFixedThreadPool:LinkedBlockingQueue() 无界阻塞队列
  • newSingleThreadExecutor: LinkedBlockingQueue() 无界阻塞队列
  • newScheduledThreadPool:new DelayedWorkQueue() 一个按超时时间升序排序的队列

8.线程间通信

public class CommunicationTest {
    public static void main(String[] args){
        Number number = new Number();
        Thread t1 = new Thread(number);
        Thread t2 = new Thread(number);
        t1.setName("线程1");
        t2.setName("线程2");
        t1.start();
        t2.start();
    }
}

class Number implements Runnable{
    private int number = 1;
    @Override
    public synchronized void run() {

        while(true){
            //使得线程交替等待以及通知交替解等待
            notify();
            if(number<10){
                System.out.println(Thread.currentThread().getName()+":"+number);
                number++;
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }else{
                break;
            }
        }
    }
}

9.Threadlocal作用

ThreadLocal的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度

10.线程池实现线程复用的原理

  • 1.线程池里执行的是任务,核心逻辑在ThreadPoolExecutor类的execute方法中,同时ThreadPoolExecutor中维护了HashSet workers;
  • 2.addWorker()方法来创建线程执行任务,如果是核心线程的任务,会赋值给Worker的firstTask属性;
  • 3.Worker实现了Runnable,本质上也是任务,核心在run()方法里;
  • 4.run()方法的执行核心runWorker(),自旋拿任务while (task != null || (task = getTask()) != null)),task是核心线程Worker的firstTask或者getTask();
  • 5.getTask()的核心逻辑:
    – 1).若当前工作线程数量大于核心线程数->说明此线程是非核心工作线程,通过poll()拿任务,未拿到任务即getTask()返回null,然后会在processWorkerExit(w, completedAbruptly)方法释放掉这个非核心工作线程的引用;
    –2).若当前工作线程数量小于核心线程数->说明此时线程是核心工作线程,通过take()拿任务
    – 3).take()方式取任务,如果队列中没有任务了会调用await()阻塞当前线程,直到新任务到来,所以核心工作线程不会被回收; 当执行execute方法里的workQueue.offer(command)时会调用Condition.singal()方法唤醒一个之前阻塞的线程,这样核心线程即可复用

在这里插入图片描述

11.线程的实现方式

  • 继承Thread类,重写run方法
  • 自定义类并实现Ruannable接口,实现run()方法
  • 通过Callable和FutureTask创建线程(重)
 class NumThread implements Callable{
    private int sum=0;//
    //可以抛出异常
    @Override
    public Object call() throws Exception {
        for(int i = 0;i<=100;i++){
            if(i % 2 == 0){
                System.out.println(Thread.currentThread().getName()+":"+i);
                sum += i;
            }
        }
        return sum;
    }
}
public class ThreadNew {
    public static void main(String[] args){
        NumThread numThread = new NumThread();
        FutureTask futureTask = new FutureTask(numThread);
        Thread t1 = new Thread(futureTask);
        t1.setName("线程1");
        t1.start();
        try {
           Object sum = futureTask.get();
           System.out.println(Thread.currentThread().getName()+":"+sum);
        } catch (ExecutionException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
  • 通过线程池创建线程

12. 设计模式

单例模式的几种写法
策略模式
适配器模式
工厂模式
构建者模式
观察者模式
代理模式(jdk代理,还会问到静态代理,以及 Spring的代理等)

13.怎么实现分库分表大数据查询

14.事务的隔离机制

15.Spring AOP

有2个类,怎么用Spring Aop 去实现以下,在这两个类中的方法,添加一些属性
Spring Aop 实现的原理,会涉及到 JDK 代理,和Spring 代理,Spring Bean的实现原理(反射,得讲详细点)
怎么用Aop实现针对不同的类中的一些方法,执行特定的代码,[
定义一个注解,然后加在要执行的类的某个方法上面,然后再利用Spring Aop 切面反射去执行]
Spring事务介绍

16.自定义注解的场景及实现

17.并发中的设计模式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值