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

​

Java 面试题解析:CountdownLatch、线程池、CAS、synchronized、Lock、多态等关键概念

在 Java 面试中,除了基础的语法知识外,面试官还会考察一些更深入的多线程编程和并发控制等高级话题。本文将详细解答一些常见的 Java 面试题,包括 CountDownLatch 、线程池的 submit 和 execute 方法 、Java 多态的实现原理 、CAS 、synchronized 和 Lock 等技术概念,并提供代码示例加以解释。​编辑


1. CountDownLatch

CountDownLatch 是一个并发工具类,它允许一个或多个线程等待直到其他线程的操作完成。它的核心概念是通过一个计数器来控制线程的等待和释放。线程在 CountDownLatch 中调用 await() 方法等待,直到计数器达到零时才会被释放。计数器通过调用 countDown() 方法递减。​编辑

使用场景:

  • 等待多个线程完成某项任务后,主线程才能继续执行。
  • 例如,在测试时等待多个线程完成工作再进行下一步操作。

示例:

java 复制代码
import java.util.concurrent.CountDownLatch;

public class TestCountDownLatch {  
    public static void main(String[] args) throws InterruptedException {  
        int threadCount = 3;  
        CountDownLatch latch = new CountDownLatch(threadCount);

        Runnable task = () -> {  
            try {  
                Thread.sleep(1000);  
                System.out.println(Thread.currentThread().getName() + " done!");  
            } catch (InterruptedException e) {  
                Thread.currentThread().interrupt();  
            } finally {  
                latch.countDown(); // 计数器减1  
            }  
        };

        for (int i = 0; i < threadCount; i++) {  
            new Thread(task).start();  
        }

        latch.await(); // 主线程等待,直到计数器为0  
        System.out.println("All threads are done!");  
    }  
}  

输出:

bash 复制代码
Thread-0 done!  
Thread-1 done!  
Thread-2 done!  
All threads are done!  

2. submit/execute ​编辑

在 Java 中,submit() 和 execute() 都是线程池(ExecutorService)的常用方法,用于提交任务给线程池执行。它们之间的主要区别在于返回值和任务处理方式。

  • execute(Runnable) :用于提交一个 Runnable 类型的任务,不会返回任务的执行结果。任务执行时,如果发生异常,异常会被吞掉。编辑
  • submit(Runnable) :用于提交一个 Runnable 类型的任务,会返回一个 Future 对象,可以通过 Future 对象获取任务的执行结果或异常。

区别总结:

  • execute 没有返回值,适合处理那些不关心任务执行结果的场景。
  • submit 返回 Future 对象,可以用于获取任务的执行状态、结果以及异常。

示例:

java 复制代码
import java.util.concurrent.*;

public class TestSubmitExecute {  
    public static void main(String[] args) throws InterruptedException, ExecutionException {  
        ExecutorService executor = Executors.newFixedThreadPool(2);

        Runnable task1 = () -> System.out.println("Task1 executed");  
        Callable<String> task2 = () -> {  
            Thread.sleep(1000);  
            return "Task2 result";  
        };

        executor.execute(task1); // 不返回结果

        Future<String> future = executor.submit(task2); // 返回 Future  
        System.out.println(future.get()); // 输出 "Task2 result"

        executor.shutdown();  
    }  
}  

3. Java 多态实现原理

Java 中的多态(Polymorphism)是指同一个方法调用,可以根据对象的不同而表现出不同的行为。Java 实现多态的原理主要通过 方法重写 和 方法动态绑定 来实现。

  • 方法重写:子类重新定义父类的方法。
  • 方法动态绑定:在运行时,根据对象的实际类型来决定调用哪个方法。

多态的实现原理:

  • 方法调用时的动态绑定:Java 使用虚拟机(JVM)中的方法调用机制,方法的调用由编译时决定调用哪一个类的版本,但是实际方法的调用是在运行时动态绑定的。

示例:

java 复制代码
class Animal {  
    public void sound() {  
        System.out.println("Animal makes sound");  
    }  
}

class Dog extends Animal {  
    @Override  
    public void sound() {  
        System.out.println("Dog barks");  
    }  
}

public class TestPolymorphism {  
    public static void main(String[] args) {  
        Animal animal = new Dog(); // 多态:父类引用指向子类对象  
        animal.sound(); // 输出 "Dog barks"  
    }  
}  

总结:Java 通过继承和方法重写实现多态,方法调用由 JVM 在运行时根据对象类型决定,称为动态绑定。


4. 线程池

线程池是为了避免频繁创建和销毁线程的开销,它通过复用线程来提高性能。Java 提供了 Executor 框架来管理线程池,常用的线程池有:

  • FixedThreadPool:线程池大小固定。
  • CachedThreadPool:线程池大小可以根据需要调整。
  • SingleThreadExecutor:单线程执行任务。
  • ScheduledThreadPool:可以定时执行任务。

线程池的常见方法有 submit 和 execute。

示例:

java 复制代码
import java.util.concurrent.*;

public class TestThreadPool {  
    public static void main(String[] args) {  
        ExecutorService executorService = Executors.newFixedThreadPool(3);

        Runnable task = () -> System.out.println(Thread.currentThread().getName() + " is executing task");

        for (int i = 0; i < 5; i++) {  
            executorService.submit(task); // 提交任务  
        }

        executorService.shutdown(); // 关闭线程池  
    }  
}  

总结:线程池通过复用线程来提高性能,避免了频繁创建销毁线程的开销。


5. CAS(Compare-And-Swap)

CAS(比较并交换)是一种无锁的并发控制技术,通常用来实现原子操作。它通过原子地比较某个内存位置的值和预期值,如果两者相等,则将该内存位置的值更新为新值。

CAS 的优点是避免了加锁机制带来的性能问题,但也存在"ABA 问题",即值从 A 变为 B 再变回 A,CAS 无法检测这种变化。

示例:

java 复制代码
import java.util.concurrent.atomic.AtomicInteger;

public class TestCAS {  
    private static AtomicInteger counter = new AtomicInteger(0);

    public static void main(String[] args) {  
        // 使用 CAS 自增  
        System.out.println("Counter: " + counter.getAndIncrement()); // 输出 0  
        System.out.println("Counter: " + counter.get()); // 输出 1  
    }  
}  

总结:CAS 是一种高效的并发控制技术,能够在不加锁的情况下实现原子操作,但需要注意一些细节问题,如 ABA 问题。


6. synchronized

synchronized 是 Java 提供的一种同步机制,用于解决多线程并发访问共享资源时的线程安全问题。它可以修饰方法或代码块,保证同一时刻只有一个线程能够执行被修饰的代码。

原理:

  • 对象锁:每个对象都有一个锁,可以通过 synchronized 修饰方法来获取锁。
  • 类锁:如果 synchronized 修饰的是静态方法,则是类级别的锁。

示例:

java 复制代码
public class TestSynchronized {  
    private int counter = 0;

    public synchronized void increment() {  
        counter++;  
    }

    public synchronized int getCounter() {  
        return counter;  
    }

    public static void main(String[] args) throws InterruptedException {  
        TestSynchronized test = new TestSynchronized();

        Runnable task = () -> {  
            for (int i = 0; i < 1000; i++) {  
                test.increment();  
            }  
        };

        Thread t1 = new Thread(task);  
        Thread t2 = new Thread(task);  
        t1.start();  
        t2.start();  
        t1.join();  
        t2.join();

        System.out.println("Counter: " + test.getCounter()); // 输出 2000  
    }  
}  

总结 :synchronized 是 Java 内置的同步机制,通过锁来保证线程安全。


7. Lock

Lock 是 Java 并发包中的接口,它提供了比 synchronized 更灵活的锁机制。Lock 可以更精确地控制锁的获取和释放,支持公平锁、重入锁等特性。

常用的实现类是 ReentrantLock。

示例:

java 复制代码
import java.util.concurrent.locks.Lock;  
import java.util.concurrent.locks.ReentrantLock;

public class TestLock {  
    private int counter = 0;  
    private Lock lock = new

​
相关推荐
码事漫谈6 小时前
黄仁勋被裁第一天,就收到了橄榄枝
后端
金銀銅鐵7 小时前
带您看懂字节码指令(二):空循环里有什么
java·后端·ai编程
皮皮林5518 小时前
比 Xshell 还好用的 SSH 客户端神器,MobaXterm 太爱了!
后端
我不会起名字3229 小时前
会话缓存失效排查:区分 Redis 失效与节点广播
redis·后端·go·即时通讯·im·故障排查
深入云栈11 小时前
Netty 4.2.x 源码深度解析 (二十三):ByteBuf 核心设计 —— 索引、类型与零拷贝
java·后端·架构
JackJiang11 小时前
IM踩坑:聊天消息ID怎么设计?一个UUID搞不定,我用双ID解决
后端·架构
开心猴爷11 小时前
iOS 上架 App 流程
后端·ios
code_Bo11 小时前
不只命令行:我把跨 Agent 会话同步做成了桌面应用
前端·javascript·后端
聪明的笨猪猪12 小时前
企业知识库AI架构选型:落地导向的架构决策与组件清单
经验分享·面试·架构·embedding·数据库架构
用户83562907805112 小时前
使用 Python 控制 Excel 工作表的分页位置
后端·python