《深入理解 Java Concurrency Book:从基础到实践》
简介
Java 并发编程在现代软件开发中扮演着至关重要的角色,特别是在处理多线程、并行任务和异步操作时。《Java Concurrency in Practice》(以下简称 Java Concurrency Book)是一本经典的书籍,它系统地介绍了 Java 并发编程的核心概念、使用方法和最佳实践。本文将围绕这本书的核心内容,为读者详细阐述 Java 并发编程的基础概念、使用方法、常见实践以及最佳实践,帮助读者更好地掌握 Java 并发编程的精髓。
目录
- Java 并发编程基础概念
- Java 并发编程使用方法
- 常见实践案例
- 最佳实践建议
- 小结
- 参考资料
1. Java 并发编程基础概念
1.1 线程与进程
- 进程:进程是程序在操作系统中的一次执行过程,是系统进行资源分配和调度的基本单位。每个进程都有自己独立的内存空间和系统资源。
- 线程:线程是进程中的一个执行单元,是 CPU 调度和分派的基本单位。一个进程可以包含多个线程,这些线程共享进程的内存空间和系统资源。
1.2 并发与并行
- 并发:指多个任务在同一时间段内交替执行,CPU 时间片在不同任务之间快速切换,给人一种同时执行的错觉。
- 并行:指多个任务在同一时刻同时执行,需要多核 CPU 的支持。
1.3 同步与异步
- 同步:指多个线程在访问共享资源时,需要按照一定的顺序依次进行,以保证数据的一致性和完整性。
- 异步:指多个线程可以同时访问共享资源,不需要等待其他线程的执行结果,提高了程序的执行效率。
1.4 锁与同步机制
- 锁:是一种用于控制多个线程对共享资源访问的机制,保证同一时刻只有一个线程可以访问共享资源。
- 同步机制:包括 synchronized 关键字、ReentrantLock 类等,用于实现线程之间的同步。
2. Java 并发编程使用方法
2.1 创建线程
Java 中创建线程有两种方式:继承 Thread 类和实现 Runnable 接口。
// 继承 Thread 类
class MyThread extends Thread {
@Override
public void run() {
System.out.println("Thread is running.");
}
}
// 实现 Runnable 接口
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("Runnable is running.");
}
}
public class ThreadCreationExample {
public static void main(String[] args) {
// 创建并启动继承 Thread 类的线程
MyThread thread = new MyThread();
thread.start();
// 创建并启动实现 Runnable 接口的线程
MyRunnable runnable = new MyRunnable();
Thread thread2 = new Thread(runnable);
thread2.start();
}
}
2.2 线程同步
使用 synchronized 关键字实现线程同步。
class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public int getCount() {
return count;
}
}
public class SynchronizationExample {
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
// 创建并启动两个线程
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
});
thread1.start();
thread2.start();
// 等待两个线程执行完毕
thread1.join();
thread2.join();
System.out.println("Final count: " + counter.getCount());
}
}
2.3 线程池
使用 ExecutorService 接口和 Executors 类创建线程池。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
class MyTask implements Runnable {
@Override
public void run() {
System.out.println("Task is running.");
}
}
public class ThreadPoolExample {
public static void main(String[] args) {
// 创建一个固定大小的线程池
ExecutorService executorService = Executors.newFixedThreadPool(2);
// 提交任务到线程池
executorService.submit(new MyTask());
executorService.submit(new MyTask());
// 关闭线程池
executorService.shutdown();
}
}
3. 常见实践案例
3.1 生产者 - 消费者模型
生产者 - 消费者模型是一种常见的并发编程模型,用于解决生产者和消费者之间的同步问题。
import java.util.LinkedList;
import java.util.Queue;
class ProducerConsumerExample {
private static final int MAX_SIZE = 5;
private static Queue<Integer> queue = new LinkedList<>();
static class Producer implements Runnable {
@Override
public void run() {
try {
while (true) {
synchronized (queue) {
while (queue.size() == MAX_SIZE) {
queue.wait();
}
int item = (int) (Math.random() * 100);
queue.add(item);
System.out.println("Produced: " + item);
queue.notifyAll();
Thread.sleep(1000);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class Consumer implements Runnable {
@Override
public void run() {
try {
while (true) {
synchronized (queue) {
while (queue.isEmpty()) {
queue.wait();
}
int item = queue.poll();
System.out.println("Consumed: " + item);
queue.notifyAll();
Thread.sleep(1000);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Thread producerThread = new Thread(new Producer());
Thread consumerThread = new Thread(new Consumer());
producerThread.start();
consumerThread.start();
}
}
3.2 并行计算
使用线程池实现并行计算。
import java.util.concurrent.*;
class ParallelComputationExample {
static class Task implements Callable<Integer> {
private int start;
private int end;
public Task(int start, int end) {
this.start = start;
this.end = end;
}
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = start; i <= end; i++) {
sum += i;
}
return sum;
}
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
int n = 100;
int numThreads = 4;
int chunkSize = n / numThreads;
ExecutorService executorService = Executors.newFixedThreadPool(numThreads);
Future<Integer>[] futures = new Future[numThreads];
for (int i = 0; i < numThreads; i++) {
int start = i * chunkSize + 1;
int end = (i == numThreads - 1) ? n : (i + 1) * chunkSize;
futures[i] = executorService.submit(new Task(start, end));
}
int totalSum = 0;
for (Future<Integer> future : futures) {
totalSum += future.get();
}
executorService.shutdown();
System.out.println("Total sum: " + totalSum);
}
}
4. 最佳实践建议
4.1 避免死锁
- 按照相同的顺序获取锁。
- 避免嵌套锁。
- 使用定时锁,避免无限等待。
4.2 合理使用线程池
- 根据任务类型和数量选择合适的线程池类型。
- 控制线程池的大小,避免资源耗尽。
4.3 减少锁的粒度
- 尽量缩小锁的范围,减少线程之间的竞争。
4.4 使用并发容器
- 使用 Java 提供的并发容器,如 ConcurrentHashMap、CopyOnWriteArrayList 等,提高并发性能。
5. 小结
本文围绕 Java Concurrency Book 介绍了 Java 并发编程的基础概念、使用方法、常见实践以及最佳实践。通过学习这些内容,读者可以更好地理解 Java 并发编程的核心思想,掌握并发编程的技巧和方法,提高程序的性能和稳定性。在实际开发中,需要根据具体需求选择合适的并发编程模型和工具,遵循最佳实践原则,避免并发编程中常见的问题。
6. 参考资料
- 《Java Concurrency in Practice》
- Java 官方文档
- 网上相关的技术博客和教程