针对“15个Java线程并发面试题和答案”的完整攻略,我会从以下几点进行讲解:
- 概述Java并发编程的基础知识;
- 解答15个与Java并发编程相关的面试题;
- 提供示例代码或实际场景说明。
1. Java并发编程基础知识
Java并发编程,是指在多个线程同时执行的情况下,协调这些线程之间的工作,保证并发的安全性与正确性。Java提供了多种并发编程的工具和方法,如线程、锁、原子类、线程池等,我们需要对这些基础知识有一定的掌握。
2. 解答15个与Java并发编程相关的面试题
以下是15个常见的Java并发编程面试题及答案,我们一一解答。
2.1 线程和进程的区别是什么?
进程是指执行中的程序,是操作系统资源分配的最小单位;线程是指进程内的执行单元,是CPU调度的最小单位。同一进程内的线程共享资源,线程之间的切换开销比进程小。
2.2 如何创建线程?
Java中创建线程的方式一般有三种,分别是继承Thread类、实现Runnable接口、使用Callable和Future接口创建有返回值的线程。
2.3 介绍一下synchronized和volatile关键字。
synchronized是java中的一种同步机制,它能够保证多个线程访问共享资源的互斥性和可见性;volatile关键字一般用于修饰变量,用来保证多个线程访问变量时的可见性和禁止指令重排序。
2.4 sleep()和wait()有什么区别?
sleep()方法是Thread类的静态方法,用来暂停当前线程一段时间;wait()方法是Object类的方法,使当前线程暂停并释放锁,直到其他线程调用notify()或notifyAll()方法将它唤醒。
2.5 join()方法的作用是什么?
join()方法是Thread类的方法,用来等待线程执行完毕。在一个线程执行了其他线程的join()方法后,它会阻塞,直到被等待的线程执行完毕。
2.6 什么是线程安全?
线程安全是指在多线程环境下,对共享资源的访问不会引发数据不一致或不正确的情况。
2.7 什么是死锁?
死锁是指多个线程都在等待其它线程释放锁而被阻塞的状态,由于都在等待对方释放锁导致程序无法继续执行。
2.8 如何避免死锁?
避免死锁的方法一般有四种:避免使用多个锁;按固定的顺序加锁;使用tryLock()方法避免线程长时间阻塞;使用定时锁等待。
2.9 什么是线程池?
线程池是一种管理线程的机制,它会预先创建一定数量的线程,放到一个线程池中等待调用。当需要执行任务时,会从线程池中提取一个线程来处理,这样可以减少线程创建和销毁的开销。
2.10 synchronized关键字在高并发下有什么问题?
synchronized关键字的同步锁,在高并发下会导致线程阻塞,降低程序性能。针对这种情况通常可以使用可重入锁或无锁编程等方式。
2.11 ReentrantLock相对于synchronized有哪些优势?
ReentrantLock相较于synchronized有更多的可操作性,如可中断、可定时、可公平、可多条件等。
2.12 什么是CAS操作?
CAS操作,即Compare and Set,是一种乐观锁的实现方式,通过无锁机制实现对共享资源的同步访问。当多个线程同时访问同一共享资源时,CAS操作首先读取共享资源的值,然后比较期望值和当前值是否一致,如果一致则修改并写入,否则重新读取并重复操作。
2.13 什么是AQS?
AQS,全称AbstractQueuedSynchronizer,是Java并发编程中一个基础性的类,通过一个FIFO队列来管理线程的访问,可以实现锁和同步器框架。
2.14 ThreadLocal的作用是什么?
ThreadLocal是Java中的一个线程级别的变量,它的作用是保证每个线程中都有一份独立的变量副本,避免线程间相互干扰。
2.15 如何优化Java中的并发性能?
优化Java中的并发性能需要考虑多方面因素,包括但不限于减少锁竞争、合理使用线程池、利用无锁编程、使用更高效的同步机制等。
3. 示例说明
针对以上面试题,我们可以通过实际场景来加深理解,比如对于线程安全的理解,可以举一个银行搞存取款的场景:
class BankAccount {
private int balance;
public BankAccount(int balance) {
this.balance = balance;
}
// 存款
public void deposit(int amount) {
balance += amount;
}
// 取款
public void withdraw(int amount) {
if (balance - amount >= 0) {
balance -= amount;
} else {
System.out.println("余额不足,取款失败");
}
}
public int getBalance() {
return balance;
}
}
上述代码中,如果多个线程同时操作存款和取款,就有可能发生线程安全问题。可以在deposit()和withdraw()方法上使用synchronized关键字来确保线程安全:
class BankAccount {
private int balance;
public BankAccount(int balance) {
this.balance = balance;
}
// 存款
public synchronized void deposit(int amount) {
balance += amount;
}
// 取款
public synchronized void withdraw(int amount) {
if (balance - amount >= 0) {
balance -= amount;
} else {
System.out.println("余额不足,取款失败");
}
}
public int getBalance() {
return balance;
}
}
此时,多个线程同时进行存取款操作时,会互斥地拿到锁,保证了线程安全性。
另外,针对线程池的理解,可以举一个大量读取文件的场景:
class FileTask implements Runnable {
private String fileName;
public FileTask(String fileName) {
this.fileName = fileName;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "开始读取文件" + fileName);
File file = new File(fileName);
try {
BufferedReader reader = new BufferedReader(new FileReader(file));
String line;
while ((line = reader.readLine()) != null) {
// TODO: 进行逻辑操作
}
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "读取文件" + fileName + "完成");
}
}
public class ThreadPoolDemo {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newFixedThreadPool(10);//创建10个线程的线程池
List<String> fileList = Arrays.asList("file1.txt", "file2.txt", "file3.txt", "file4.txt", "file5.txt");
for (String fileName : fileList) {
threadPool.execute(new FileTask(fileName));//提交任务
}
threadPool.shutdown();//关闭线程池
}
}
上述代码中,使用线程池优化了大量的文件读取任务,保证了程序的并发性和高效性。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:15个Java线程并发面试题和答案 - Python技术站