跳转至

《深入理解 Java Concurrency Book:从基础到实践》

简介

Java 并发编程在现代软件开发中扮演着至关重要的角色,特别是在处理多线程、并行任务和异步操作时。《Java Concurrency in Practice》(以下简称 Java Concurrency Book)是一本经典的书籍,它系统地介绍了 Java 并发编程的核心概念、使用方法和最佳实践。本文将围绕这本书的核心内容,为读者详细阐述 Java 并发编程的基础概念、使用方法、常见实践以及最佳实践,帮助读者更好地掌握 Java 并发编程的精髓。

目录

  1. Java 并发编程基础概念
  2. Java 并发编程使用方法
  3. 常见实践案例
  4. 最佳实践建议
  5. 小结
  6. 参考资料

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 官方文档
  • 网上相关的技术博客和教程