Java 并发与并行的科普

引言

在现代计算中,并发和并行是提高程序性能的重要手段,尤其在涉及到多处理器或多核系统时。Java 作为一种广泛使用的编程语言,提供了强大的并发和并行支持。本文将对 Java 中的并发和并行进行深入介绍,并给出相应的代码示例和可视化内容。

并发与并行的概念

并发

并发是指多个任务在同一时间段内进行,但并不一定是同时执行。也可以理解为任务在一段时间内交替进行。例如,在一个单核 CPU 上,多个线程可以在短时间内轮流执行。

并行

并行则是指多个任务在同一时刻真正同时执行。这通常发生在多核处理器上,CPU 可以同时处理多个线程或任务。

可以想象一下,若我们有一个厨房,多个厨师(线程)在同一时间为不同客户(任务)做饭,这就属于并行。而如果这些厨师共享同一台灶台进行排队做饭,这就属于并发。

Java中的并发与并行支持

Java 提供了多线程和并发的强大支持,主要通过以下两种方式来实现:

  1. Thread 类
  2. Runnable 接口

此外,Java 还提供了一些并发工具包 java.util.concurrent,如 ExecutorSemaphoreCountDownLatch 等。

使用 Thread 类进行并发

接下来,我们通过代码示例来了解 Java 中的并发实现。

class MyThread extends Thread {
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + " - " + i);
            try {
                Thread.sleep(100); // 模拟其他操作
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class ThreadExample {
    public static void main(String[] args) {
        MyThread thread1 = new MyThread();
        MyThread thread2 = new MyThread();
        thread1.start();
        thread2.start();
    }
}

在这个例子中,我们定义了一个 MyThread 类,继承自 Thread 类,并重写了 run 方法。在主方法中,我们创建了两个线程并启动它们。你将会看到它们交替输出内容,这是并发的体现。

使用 Runnable 接口进行并发

另一种常用的方式是实现 Runnable 接口。

class MyRunnable implements Runnable {
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + " - " + i);
            try {
                Thread.sleep(100); // 模拟其他操作
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class RunnableExample {
    public static void main(String[] args) {
        Thread thread1 = new Thread(new MyRunnable());
        Thread thread2 = new Thread(new MyRunnable());
        thread1.start();
        thread2.start();
    }
}

在这个例子中,MyRunnable 类实现了 Runnable 接口,main 方法中创建并启动了多个线程。输出内容也表现出并发的特性。

Java 并行编程

Java 还支持更复杂的并行编程,如使用 Fork/Join 框架。下面是一个示例,展示如何使用 Fork/Join 框架来并行计算一个数组的总和。

import java.util.concurrent.RecursiveTask;
import java.util.concurrent.ForkJoinPool;

public class ForkJoinExample extends RecursiveTask<Long> {
    private static final int THRESHOLD = 1000;
    private final long[] array;
    private final int start;
    private final int end;

    public ForkJoinExample(long[] array, int start, int end) {
        this.array = array;
        this.start = start;
        this.end = end;
    }

    @Override
    protected Long compute() {
        if (end - start <= THRESHOLD) {
            long sum = 0;
            for (int i = start; i < end; i++) {
                sum += array[i];
            }
            return sum;
        } else {
            int mid = (start + end) / 2;
            ForkJoinExample leftTask = new ForkJoinExample(array, start, mid);
            ForkJoinExample rightTask = new ForkJoinExample(array, mid, end);
            leftTask.fork(); // 异步处理左半部分
            long rightResult = rightTask.compute(); // 同步处理右半部分
            long leftResult = leftTask.join(); // 等待左半部分结果
            return leftResult + rightResult;
        }
    }

    public static void main(String[] args) {
        long[] array = new long[10000];
        for (int i = 0; i < array.length; i++) {
            array[i] = i + 1;
        }
        ForkJoinPool pool = new ForkJoinPool();
        ForkJoinExample task = new ForkJoinExample(array, 0, array.length);
        long result = pool.invoke(task);
        System.out.println("Total sum: " + result);
    }
}

在这个示例中,我们使用 ForkJoinPool 来实现对一个数组的并行求和。在数据量大于某个阈值时,任务会被分割以便并行执行。

甘特图可视化与流程图

为了便于理解并发和并行的过程,我们可以使用甘特图和流程图进行可视化。

甘特图

gantt
    title 并发与并行甘特图
    dateFormat  YYYY-MM-DD
    section 任务1
    线程1 :a1, 2023-10-01, 3d
    线程2 :after a1  , 3d

流程图

flowchart TD
    A[开始] --> B{是并发还是并行?}
    B -->|并发| C[使用Thread类或Runnable接口]
    B -->|并行| D[使用Fork/Join框架]
    C --> E[执行任务]
    D --> E
    E --> F[结束]

结尾

本文介绍了 Java 中的并发与并行的基本概念及其实现方式,展示了相应的代码示例,帮助大家理解如何在 Java 中有效地进行并发与并行编程。希望通过这一系列的介绍,您能够在编写高效程序的过程中,灵活运用并发和并行的机制,提升程序性能。随着对多线程和并行处理的深入理解,您将能够在数据密集型和计算密集型的应用中更有效地利用系统资源。