面试真实经历某商银行大厂Java问题和答案总结(五)

​

Java 面试常见问题解析​编辑

Java 中的多线程和类加载机制是开发中非常重要的部分。在面试过程中,涉及线程管理和 JVM 的相关问题经常出现。本文将探讨一些 Java 面试中的常见问题,并为每个问题提供详细的解答。

​编辑---

1. 线程的同步方法

线程的同步方法是指通过某些机制来控制多线程环境中多个线程对共享资源的访问,以防止数据冲突。以下是几种常见的同步方法:

  1. synchronized 关键字 :
    synchronized 关键字可以用来修饰方法或代码块,确保同一时刻只有一个线程能够访问被修饰的方法或代码块。

    • 同步方法 :方法声明时使用 synchronized 关键字,这样该方法的代码会被同步执行。

    java public synchronized void syncMethod() { // 线程安全的代码 }

  2. Lock 接口(ReentrantLock) :
    ReentrantLock 是 Java 提供的显式锁,它比 synchronized 更加灵活,可以实现更细粒度的同步控制,且提供了尝试锁和超时锁等特性。​编辑

    java Lock lock = new ReentrantLock(); try { lock.lock(); // 线程安全的代码 } finally { lock.unlock(); }

  3. 原子变量(Atomic 类) :
    Atomic 类(如 AtomicInteger)提供了原子操作的方法,可以确保对基本类型的原子性操作,避免了多线程竞争条件的问题。

    java AtomicInteger counter = new AtomicInteger(0); counter.incrementAndGet(); // 原子性自增


2. 几种方法创建线程

在 Java 中,有多种方式可以创建和启动线程,以下是最常见的三种方式:

  1. 继承 Thread 类 :

    创建一个继承 Thread 类的子类,重写 run 方法,并调用 start 启动线程。

    java class MyThread extends Thread { @Override public void run() { System.out.println("Thread is running"); } } MyThread thread = new MyThread(); thread.start();

  2. 实现 Runnable 接口 :

    实现 Runnable 接口并将其传递给 Thread 类的构造方法,适用于多个线程共享同一个 Runnable 实现的情况。

    java class MyRunnable implements Runnable { @Override public void run() { System.out.println("Runnable thread is running"); } } Thread thread = new Thread(new MyRunnable()); thread.start();

  3. 使用线程池(ExecutorService) :

    使用 ExecutorService 可以创建一个线程池来管理线程的创建与销毁,避免了手动管理线程的麻烦。

    java ExecutorService executorService = Executors.newFixedThreadPool(10); executorService.submit(() -> System.out.println("Thread pool task")); executorService.shutdown();


3. 线程的几种状态

Java 线程在生命周期中会经历不同的状态,以下是线程的几种常见状态:

  1. New(新建状态) :

    线程创建后尚未调用 start() 方法时处于 New 状态。

    java Thread t = new Thread(); System.out.println(t.getState()); // 输出:NEW

  2. Runnable(可运行状态) :

    线程调用了 start() 方法后进入 Runnable 状态,此时线程在 JVM 中排队等待 CPU 时间片来执行。

  3. Blocked(阻塞状态) :

    当线程因为等待获取某个锁而无法继续执行时,它进入 Blocked 状态。

  4. Waiting(等待状态) :

    线程通过调用 wait()、join() 或 LockSupport.park() 等方法进入 Waiting 状态,直到被唤醒。

  5. Timed Waiting(定时等待状态) :

    线程处于 sleep()、join(time) 或 wait(time) 等方法指定的时间范围内时,会进入定时等待状态。

  6. Terminated(终止状态) :

    线程的 run() 方法执行完毕后或者被异常终止时,线程进入 Terminated 状态。


4. 线程如何从运行到挂起

线程在运行时,可以通过某些机制挂起,通常是因为等待某些条件或资源:

  • 通过调用 Thread.sleep() :

    线程调用 sleep 方法会使其进入 Timed Waiting 状态,经过指定时间后自动恢复到 Runnable 状态。

    java try { Thread.sleep(1000); // 线程挂起1秒钟 } catch (InterruptedException e) { e.printStackTrace(); }

  • 通过调用 Object.wait() :

    线程通过 wait() 方法进入 Waiting 状态,直到其他线程调用同一个对象的 notify() 或 notifyAll() 方法来唤醒它。

    java synchronized (lock) { lock.wait(); // 当前线程进入等待状态 }


5. 动态代理

动态代理是指在运行时动态生成一个代理对象,并指定其代理行为。Java 中的动态代理有两种方式:

  1. 基于接口的动态代理 (通过 Proxy 类):
    使用 Java 提供的 Proxy 类和 InvocationHandler 接口来实现动态代理。通常用于为接口创建代理。

    ```java
    public interface Hello {
    void sayHello();
    }

public class HelloImpl implements Hello {

public void sayHello() {

System.out.println("Hello, World!");

}

}

public class HelloProxy implements InvocationHandler {

private Object target;

public HelloProxy(Object target) {

this.target = target;

}

@Override

public Object invoke(Object proxy, Method method, Object\[\] args) throws Throwable {

System.out.println("Before method call");

return method.invoke(target, args);

}

}

public class ProxyDemo {

public static void main(String\[\] args) {

Hello hello = new HelloImpl();

Hello proxy = (Hello) Proxy.newProxyInstance(

hello.getClass().getClassLoader(),

hello.getClass().getInterfaces(),

new HelloProxy(hello)

);

proxy.sayHello();

}

}

```

  1. 基于类的动态代理 (通过 CGLIB):
    使用第三方库 CGLIB 实现类的动态代理,它不要求目标类实现接口。

    java Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(HelloImpl.class); enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("Before method call"); return proxy.invokeSuper(obj, args); } }); HelloImpl hello = (HelloImpl) enhancer.create(); hello.sayHello();


6. Java 类加载如何进行

Java 的类加载是由 ClassLoader 完成的,Java 采用了双亲委派模型来加载类。其基本流程如下:

  1. 加载 :通过类加载器将 .class 文件加载到 JVM 中。
  2. 连接 :
    • 验证 :确保类的字节码符合 Java 语言规范。
    • 准备 :为类的静态变量分配内存并初始化默认值。
    • 解析:将符号引用转换为直接引用。
  3. 初始化:执行类的静态初始化块和静态变量初始化。

7. Java 类加载的几种状态

Java 类加载有以下几种状态:

  1. 加载(Loading):类文件从磁盘加载到内存中。
  2. 链接(Linking):包括验证、准备和解析过程。
  3. 初始化(Initialization):执行静态初始化块,初始化类的静态变量。

8. Java JVM 中 GC 如何进行线程是否需要回收的判断

**垃圾回收(GC)**是 Java 的 JVM 负责的自动内存管理机制。GC 会根据对象的可达性来判断是否需要回收:

  1. 可达性分析:通过从根对象(如栈、方法区等)开始遍历,检查对象是否可达。如果一个对象不可达,则说明该对象可以被回收。
  2. 引用计数法 :通过记录对象的引用次数来判断其是否可回收。当引用次数为 0 时,对象可以被回收。编辑

JVM 中的垃圾回收算法通常有 标记-清除 、复制算法 、分代收集 等。


通过掌握这些 Java 多线程、类加载、代理和 GC 的基础概念.

​

相关推荐
苍何14 小时前
DeepSeek的Super-agent 设计,和我们的工程实践
后端
Patrick_Wilson15 小时前
第一次读 Go 项目:从两个源码文件弄清 package、import 和 go.mod
后端·go·编程语言
Patrick_Wilson15 小时前
执行 go build . 后,Go 怎样找到代码并生成程序
后端·go·编程语言
用户69190268133915 小时前
Redis 2:Redis 在怎么处理harness记忆
后端
Patrick_Wilson15 小时前
Go 变量不赋值时,为什么也有值
后端·go·编程语言
Patrick_Wilson15 小时前
读 Go 项目里的命令:run、build、test 分别做了什么
后端·go·编程语言
程序员柒叔15 小时前
Diagram Design 解构:45 种图型如何用语义路由与 SVG 质量门交付
人工智能·github·agent·skills
逛逛GitHub15 小时前
9 个神级 GitHub 项目助你蹭上抖音、小红书 Vibe 知识大赏热点。
github
m4Rk_16 小时前
【论文阅读】Agent 记忆机制(119):EvoLife——让具身 Agent 把主动问到的答案沉淀为可复用的空间经验
论文阅读·人工智能·学习·开源·github
程序猿乐锅16 小时前
万字详解Single-flight(单飞模式)
java·开发语言·分布式·后端·学习·single-flight