面试题答案
一键面试线程池与线程安全机制的协同工作
- 任务提交:
- 当客户端连接请求到达时,线程池的阻塞队列接收这些任务。由于阻塞队列本身是线程安全的(如
LinkedBlockingQueue
),多个线程可以安全地向队列中添加任务(offer
或put
方法)。例如:
BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(10); ThreadPoolExecutor executor = new ThreadPoolExecutor( 5, 10, 10L, TimeUnit.SECONDS, queue); executor.submit(() -> { // 处理客户端连接请求的代码 });
- 线程池中的工作线程从阻塞队列中获取任务(
take
方法)。阻塞队列的设计确保了在多线程环境下,获取任务的操作也是线程安全的,避免了竞争条件。
- 当客户端连接请求到达时,线程池的阻塞队列接收这些任务。由于阻塞队列本身是线程安全的(如
- 线程复用:
- 线程池维护一定数量的工作线程,这些线程被复用处理不同的客户端请求。线程安全机制保证了每个工作线程在执行任务时不会相互干扰。例如,每个线程独立执行自己从队列中取出的任务,不会出现一个线程修改另一个线程正在处理的数据的情况。
- 工作线程执行任务时,可能会访问共享资源(如数据库连接池等)。这时就需要通过锁机制(如
synchronized
关键字、ReentrantLock
等)来保证对共享资源的访问是线程安全的。例如:
private static final ReentrantLock lock = new ReentrantLock(); public void accessSharedResource() { lock.lock(); try { // 访问共享资源的代码 } finally { lock.unlock(); } }
可能遇到的线程安全问题及解决方法
- 竞争条件:
- 问题描述:多个线程同时访问和修改共享资源,导致数据不一致。例如,在统计客户端连接数时,如果多个线程同时进行自增操作,可能会丢失部分计数。
- 解决方法:使用锁机制(如
synchronized
关键字或ReentrantLock
)对共享资源的访问进行同步。例如:
private int clientCount = 0; public synchronized void incrementClientCount() { clientCount++; }
- 也可以使用原子类(如
AtomicInteger
),它内部使用CAS(Compare - And - Swap)操作实现线程安全的自增等操作。例如:
private AtomicInteger clientCount = new AtomicInteger(0); public void incrementClientCount() { clientCount.incrementAndGet(); }
- 死锁:
- 问题描述:两个或多个线程相互等待对方释放资源,导致所有线程都无法继续执行。例如,线程A持有锁1并等待锁2,线程B持有锁2并等待锁1。
- 解决方法:
- 避免嵌套锁,尽量减少锁的使用范围。
- 给锁设置超时时间,使用
tryLock
方法尝试获取锁,并在一定时间内获取不到时放弃。例如:
ReentrantLock lock1 = new ReentrantLock(); ReentrantLock lock2 = new ReentrantLock(); boolean success = false; try { success = lock1.tryLock(100, TimeUnit.MILLISECONDS); if (success) { success = lock2.tryLock(100, TimeUnit.MILLISECONDS); if (success) { // 执行需要锁1和锁2的代码 } } } catch (InterruptedException e) { // 处理中断 } finally { if (success) { lock2.unlock(); } if (lock1.isHeldByCurrentThread()) { lock1.unlock(); } }
- 资源泄漏:
- 问题描述:线程在获取资源(如数据库连接、文件句柄等)后,由于异常或错误没有正确释放资源,导致资源无法被其他线程使用。
- 解决方法:使用
try - finally
块确保资源在使用后被正确释放。例如,在获取数据库连接时:
Connection conn = null; try { conn = DriverManager.getConnection(url, username, password); // 使用连接执行数据库操作 } catch (SQLException e) { // 处理异常 } finally { if (conn != null) { try { conn.close(); } catch (SQLException e) { // 处理关闭连接时的异常 } } }
- 或者使用Java 7引入的
try - with - resources
语句,它会自动关闭实现了AutoCloseable
接口的资源。例如:
try (Connection conn = DriverManager.getConnection(url, username, password)) { // 使用连接执行数据库操作 } catch (SQLException e) { // 处理异常 }