引言
并发编程是Java面试中的重点和难点,涉及线程、锁、内存模型等复杂概念。本文精选了最常见的并发编程面试题,并提供详细的解析和代码示例。
一、线程基础
1.1 创建线程的几种方式
方式1:继承Thread类
1 2 3 4 5 6 7 8 9 10
| class MyThread extends Thread { @Override public void run() { System.out.println("Thread running: " + getName()); } }
MyThread thread = new MyThread(); thread.start();
|
方式2:实现Runnable接口
1 2 3 4 5 6 7 8 9 10
| class MyRunnable implements Runnable { @Override public void run() { System.out.println("Runnable running"); } }
Thread thread = new Thread(new MyRunnable()); thread.start();
|
方式3:实现Callable接口
1 2 3 4 5 6 7 8 9 10 11
| class MyCallable implements Callable<String> { @Override public String call() throws Exception { return "Callable result"; } }
FutureTask<String> future = new FutureTask<>(new MyCallable()); new Thread(future).start(); String result = future.get();
|
方式4:使用线程池
1 2 3 4
| ExecutorService executor = Executors.newFixedThreadPool(10); executor.submit(() -> { System.out.println("Thread pool task"); });
|
对比总结:
| 方式 |
优点 |
缺点 |
适用场景 |
| 继承Thread |
简单直观 |
不能继承其他类 |
简单任务 |
| 实现Runnable |
可以继承其他类 |
无返回值 |
一般任务 |
| 实现Callable |
有返回值,可抛异常 |
稍复杂 |
需要返回值的任务 |
| 线程池 |
复用线程,管理方便 |
需要额外配置 |
生产环境 |
1.2 线程的生命周期
1 2 3 4 5 6 7 8 9 10 11 12
| start() NEW ---------> RUNNABLE <-------> RUNNING | | | | wait()/sleep()/join() | v | BLOCKED / WAITING | | |<-----------------+ | | 执行完成或异常 v TERMINATED
|
状态说明:
1 2 3 4 5 6 7 8
| public enum State { NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED }
|
状态转换示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| Thread thread = new Thread(() -> { synchronized(lock) { try { lock.wait(1000); } catch (InterruptedException e) { e.printStackTrace(); } } });
System.out.println(thread.getState());
thread.start();
System.out.println(thread.getState());
Thread.sleep(100);
System.out.println(thread.getState());
thread.join();
System.out.println(thread.getState());
|
二、synchronized关键字
2.1 synchronized的三种使用方式
1. 修饰实例方法
1 2 3 4 5 6 7 8 9 10
| public synchronized void method() { }
public void method() { synchronized(this) { } }
|
2. 修饰静态方法
1 2 3 4 5 6 7 8 9 10
| public static synchronized void staticMethod() { }
public static void staticMethod() { synchronized(MyClass.class) { } }
|
3. 修饰代码块
1 2 3 4 5
| public void method() { synchronized(lock) { } }
|
2.2 synchronized的底层原理
字节码层面:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public void syncMethod() { synchronized(this) { count++; } }
public void syncMethod(); Code: 0: aload_0 1: dup 2: astore_1 3: monitorenter 4: aload_0 5: dup 6: getfield #2 9: iconst_1 10: iadd 11: putfield #2 14: aload_1 15: monitorexit
|
实现原理:
- 基于Monitor对象
- 使用
monitorenter和monitorexit指令
- 每个对象都有一个Monitor
- 线程获取Monitor才能进入同步代码块
2.3 synchronized的锁升级机制
锁的四种状态:
1
| 无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁
|
1. 偏向锁
2. 轻量级锁
3. 重量级锁
三、volatile关键字
3.1 volatile的作用
作用1:保证可见性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class VisibilityDemo { private volatile boolean flag = false; public void writer() { flag = true; } public void reader() { while (!flag) { } System.out.println("Flag is true now!"); } }
|
作用2:禁止指令重排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class Singleton { private volatile static Singleton instance; public static Singleton getInstance() { if (instance == null) { synchronized(Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
|
3.2 volatile不能保证原子性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class VolatileAtomicityDemo { private volatile int count = 0; public void increment() { count++; } }
class AtomicDemo { private AtomicInteger count = new AtomicInteger(0); public void increment() { count.incrementAndGet(); } }
|
3.3 synchronized vs volatile
| 特性 |
synchronized |
volatile |
| 原子性 |
✓ |
✗ |
| 可见性 |
✓ |
✓ |
| 有序性 |
✓ |
✓ |
| 适用场景 |
多个操作需要原子性 |
单个变量读写 |
| 性能 |
较重 |
较轻 |
四、Lock接口
4.1 ReentrantLock vs synchronized
ReentrantLock示例:
1 2 3 4 5 6 7 8 9 10 11 12
| class LockDemo { private final ReentrantLock lock = new ReentrantLock(); public void method() { lock.lock(); try { } finally { lock.unlock(); } } }
|
功能对比:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| lock.lockInterruptibly();
if (lock.tryLock()) { try { } finally { lock.unlock(); } } else { }
if (lock.tryLock(1, TimeUnit.SECONDS)) { }
ReentrantLock fairLock = new ReentrantLock(true);
Condition condition1 = lock.newCondition(); Condition condition2 = lock.newCondition();
|
4.2 ReadWriteLock读写锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| class ReadWriteLockDemo { private final ReadWriteLock rwLock = new ReentrantReadWriteLock(); private final Lock readLock = rwLock.readLock(); private final Lock writeLock = rwLock.writeLock(); private String data = ""; public String read() { readLock.lock(); try { return data; } finally { readLock.unlock(); } } public void write(String newData) { writeLock.lock(); try { data = newData; } finally { writeLock.unlock(); } } }
|
读写锁规则:
五、线程池
5.1 线程池的核心参数
1 2 3 4 5 6 7 8 9
| public ThreadPoolExecutor( int corePoolSize, // 核心线程数 int maximumPoolSize, // 最大线程数 long keepAliveTime, // 空闲线程存活时间 TimeUnit unit, // 时间单位 BlockingQueue<Runnable> workQueue, // 任务队列 ThreadFactory threadFactory, // 线程工厂 RejectedExecutionHandler handler // 拒绝策略 )
|
执行流程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| 提交任务 | v 核心线程数 < corePoolSize? |--是--> 创建核心线程执行 | v--否 | 任务队列是否已满? |--否--> 加入队列等待 | v--是 | 线程数 < maximumPoolSize? |--是--> 创建非核心线程执行 | v--否 | 执行拒绝策略
|
5.2 常见线程池类型
1 2 3 4 5 6 7 8 9 10 11
| ExecutorService fixed = Executors.newFixedThreadPool(10);
ExecutorService cached = Executors.newCachedThreadPool();
ExecutorService single = Executors.newSingleThreadExecutor();
ScheduledExecutorService scheduled = Executors.newScheduledThreadPool(5);
|
不推荐使用Executors创建线程池的原因:
1 2 3 4 5 6
|
new LinkedBlockingQueue<Runnable>()
|
推荐手动创建:
1 2 3 4 5 6 7 8
| ThreadPoolExecutor executor = new ThreadPoolExecutor( 10, 20, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100), new ThreadPoolExecutor.CallerRunsPolicy() );
|
5.3 四种拒绝策略
1 2 3 4 5 6 7 8 9 10 11
| new ThreadPoolExecutor.AbortPolicy()
new ThreadPoolExecutor.CallerRunsPolicy()
new ThreadPoolExecutor.DiscardPolicy()
new ThreadPoolExecutor.DiscardOldestPolicy()
|
六、并发容器
6.1 ConcurrentHashMap
JDK 1.8实现原理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
final V putVal(K key, V value, boolean onlyIfAbsent) { for (Node<K,V>[] tab = table;;) { if ((f = tabAt(tab, i)) == null) { if (casTabAt(tab, i, null, node)) break; } else { synchronized (f) { } } } }
|
6.2 CopyOnWriteArrayList
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public boolean add(E e) { final ReentrantLock lock = this.lock; lock.lock(); try { Object[] elements = getArray(); int len = elements.length; Object[] newElements = Arrays.copyOf(elements, len + 1); newElements[len] = e; setArray(newElements); return true; } finally { lock.unlock(); } }
|
特点:
- 写时复制,读不加锁
- 适合读多写少场景
- 内存占用较大
6.3 BlockingQueue
常见实现:
1 2 3 4 5 6 7 8 9 10 11
| BlockingQueue<String> queue1 = new ArrayBlockingQueue<>(100);
BlockingQueue<String> queue2 = new LinkedBlockingQueue<>();
BlockingQueue<String> queue3 = new PriorityBlockingQueue<>();
BlockingQueue<String> queue4 = new SynchronousQueue<>();
|
核心方法:
1 2 3 4 5 6 7 8 9 10 11
| queue.put("item"); String item = queue.take();
queue.offer("item", 1, TimeUnit.SECONDS); String item = queue.poll(1, TimeUnit.SECONDS);
queue.add("item"); String item = queue.remove();
|
七、AQS原理
7.1 AQS核心概念
1 2 3 4 5 6 7 8
| public abstract class AbstractQueuedSynchronizer { private volatile int state; private transient volatile Node head; private transient volatile Node tail; }
|
基于AQS的同步器:
- ReentrantLock
- Semaphore
- CountDownLatch
- CyclicBarrier
- ReentrantReadWriteLock
7.2 AQS的两种模式
1. 独占模式(Exclusive)
1 2 3 4 5 6 7 8
| public void lock() { sync.acquire(1); }
public void unlock() { sync.release(1); }
|
2. 共享模式(Shared)
1 2 3 4 5 6 7 8
| public void acquire() { sync.acquireShared(1); }
public void release() { sync.releaseShared(1); }
|
八、常见并发工具类
8.1 CountDownLatch
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| CountDownLatch latch = new CountDownLatch(3);
for (int i = 0; i < 3; i++) { new Thread(() -> { System.out.println("Task done"); latch.countDown(); }).start(); }
latch.await(); System.out.println("All tasks completed");
|
8.2 CyclicBarrier
1 2 3 4 5 6 7 8 9 10 11 12
| CyclicBarrier barrier = new CyclicBarrier(3, () -> { System.out.println("All threads arrived"); });
for (int i = 0; i < 3; i++) { new Thread(() -> { System.out.println("Thread " + Thread.currentThread().getName() + " arrived"); barrier.await(); System.out.println("Thread " + Thread.currentThread().getName() + " continued"); }).start(); }
|
8.3 Semaphore
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < 10; i++) { new Thread(() -> { semaphore.acquire(); try { System.out.println("Working..."); Thread.sleep(1000); } finally { semaphore.release(); } }).start(); }
|
总结
Java并发编程的核心知识点:
- 线程基础:创建方式、生命周期
- synchronized:使用方式、底层原理、锁升级
- volatile:可见性、有序性、不保证原子性
- Lock:ReentrantLock、ReadWriteLock
- 线程池:核心参数、执行流程、拒绝策略
- 并发容器:ConcurrentHashMap、CopyOnWriteArrayList
- AQS:独占模式、共享模式
- 工具类:CountDownLatch、CyclicBarrier、Semaphore
参考资料