1. 多线程进阶
- 关键技术
- synchronized:实现锁优化,如锁升级、锁粗化、锁消除等。它是基于JVM实现的内置锁,通过互斥方式保证同一时间只有一个线程能进入同步代码块 。例如:
public class SynchronizedExample {
private int count = 0;
public synchronized void increment() {
count++;
}
}
- CAS(Compare and Swap,比较并交换):原子类的基础操作,是一种无锁算法。它包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。只有当预期原值A和内存位置V的值相同时,才会将内存位置V的值更新为B 。在Java中, java.util.concurrent.atomic 包下的原子类广泛应用了CAS操作,比如 AtomicInteger :
import java.util.concurrent.atomic.AtomicInteger;
public class CASExample {
private AtomicInteger atomicInteger = new AtomicInteger(0);
public void increment() {
while (true) {
int current = atomicInteger.get();
if (atomicInteger.compareAndSet(current, current + 1)) {
break;
}
}
}
}
- JUC(java.util.concurrent) 包:提供了丰富的并发工具类。
- Callable / FutureTask:Callable接口允许线程返回一个结果,FutureTask可以包装Callable,用于获取异步计算的结果。示例:
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class CallableExample {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Callable<Integer> callable = () -> {
int sum = 0;
for (int i = 0; i < 100; i++) {
sum += i;
}
return sum;
};
FutureTask<Integer> futureTask = new FutureTask<>(callable);
Thread thread = new Thread(futureTask);
thread.start();
System.out.println(futureTask.get());
}
}
- ReentrantLock:可重入锁,相比synchronized提供了更灵活的锁控制,如可中断锁获取、公平锁等。使用示例:
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockExample {
private ReentrantLock lock = new ReentrantLock();
public void doSomething() {
lock.lock();
try {
// 业务逻辑
} finally {
lock.unlock();
}
}
}
2. Semaphore(信号量)
- 原理:本质是一个计数器,用于控制“可用资源”的数量。通过 acquire() 方法申请资源(计数器减1), release() 方法释放资源(计数器加1) 。当计数器为0时,再调用 acquire() 方法的线程会被阻塞。
- 代码示例:模拟停车场停车场景,假设有3个停车位。
import java.util.concurrent.Semaphore;
public class SemaphoreExample {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(3);
for (int i = 1; i <= 5; i++) {
new Thread(() -> {
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + " 进入停车场");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 离开停车场");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
}
}, "车辆" + i).start();
}
}
}
3. CountDownLatch
- 作用:允许一个或多个线程等待其他一组线程完成操作。通过构造函数指定需要等待的线程数量,每个完成任务的线程调用 countDown() 方法将计数器减1 ,等待的线程通过 await() 方法阻塞,直到计数器为0 。
- 代码示例:模拟比赛开始,所有选手准备好后比赛开始。
import java.util.concurrent.CountDownLatch;
public class CountDownLatchExample {
public static void main(String[] args) {
int numOfPlayers = 5;
CountDownLatch countDownLatch = new CountDownLatch(numOfPlayers);
for (int i = 1; i <= numOfPlayers; i++) {
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + " 准备完毕");
Thread.sleep((long) (Math.random() * 3000));
countDownLatch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "选手" + i).start();
}
try {
countDownLatch.await();
System.out.println("比赛开始!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
4. 多线程环境下的集合类
- ArrayList在多线程环境的问题及解决:ArrayList本身不是线程安全的。在多线程环境下并发读写会出现数据不一致等问题。可以使用 Collections.synchronizedList(new ArrayList<>()) 将其转换为线程安全的列表,它是通过在关键方法(如add、get等)上加 synchronized 关键字实现同步控制 ;也可以使用 CopyOnWriteArrayList ,它在写操作时会复制底层数组,读操作无锁,适合读多写少的场景。
- HashMap在多线程环境的问题及解决:HashMap在多线程环境下扩容时可能会出现死循环等问题。可以使用 Hashtable (它的方法都是 synchronized 修饰的,线程安全但性能开销大) ;或者使用 ConcurrentHashMap ,它采用分段锁机制,允许多个线程同时读,部分线程写,大大提高了并发性能 。例如获取 ConcurrentHashMap 中元素:
import java.util.concurrent.ConcurrentHashMap;
public class ConcurrentHashMapExample {
public static void main(String[] args) {
ConcurrentHashMap<Integer, String> map = new ConcurrentHashMap<>();
map.put(1, "one");
map.put(2, "two");
System.out.println(map.get(1));
}
}