如何理解Java中的并发?

Java 中的并发(Concurrency) 指多个任务在同一时间段内交替执行(宏观上同时进行,微观上可能是 CPU 快速切换调度),目的是提高程序效率,充分利用系统资源(如 CPU、内存、I/O 等)。

一、为什么需要并发?

  1. 资源利用率最大化

    当程序执行 I/O 操作(如读写文件、网络请求)时,CPU 通常处于空闲状态。通过并发,可在等待 I/O 时让 CPU 处理其他任务,避免资源浪费。

    例如:一个下载文件的程序,在等待网络数据时,可同时解析已下载的部分数据。

  2. 响应速度提升

    对于交互式程序(如 GUI 应用、服务器),并发能避免单任务阻塞导致的界面卡顿或请求超时。

    例如:Web 服务器同时处理多个用户的请求,而非逐个排队处理。

二、并发的核心概念

1. 线程(Thread)与进程(Process)
  • 进程:程序的一次执行过程,是系统资源分配的基本单位(有独立的内存空间)。
  • 线程:进程内的执行单元,是 CPU 调度的基本单位(共享进程的内存空间)。
  • 关系:一个进程可包含多个线程(多线程),线程间切换成本远低于进程切换。
2. 并行(Parallelism)与并发(Concurrency)的区别
  • 并发:多个任务"交替执行"(CPU 切换速度快,看起来同时进行),适用于单 CPU 或多 CPU。
  • 并行 :多个任务"同时执行"(需多 CPU 核心,每个核心处理一个任务)。
    例如:4 核 CPU 同时运行 4 个线程是并行,1 核 CPU 快速切换 4 个线程是并发。

三、Java 实现并发的方式

Java 提供了多种并发编程工具,核心是通过线程实现:

1. 基础方式
  • 继承 Thread 类 :重写 run() 方法定义任务,调用 start() 启动线程。

  • 实现 Runnable 接口 :定义任务逻辑,通过 Thread 类包装并启动(推荐,避免单继承限制)。

  • 实现 Callable 接口 :与 Runnable 类似,但可返回结果并抛出异常,配合 Future 获取结果。

    java 复制代码
    // Callable 示例
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.FutureTask;
    
    public class CallableDemo {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            // 1. 定义任务(有返回值)
            Callable<Integer> task = () -> {
                int sum = 0;
                for (int i = 0; i <= 100; i++) {
                    sum += i;
                }
                return sum;
            };
    
            // 2. 包装任务
            FutureTask<Integer> futureTask = new FutureTask<>(task);
    
            // 3. 启动线程
            new Thread(futureTask).start();
    
            // 4. 获取结果(会阻塞直到任务完成)
            System.out.println("1-100的和:" + futureTask.get()); // 输出5050
        }
    }
2. 线程池(ThreadPoolExecutor)

频繁创建/销毁线程会消耗资源,线程池 通过复用线程提高效率,是生产环境的首选。

Java 提供 Executors 工具类快速创建线程池:

java 复制代码
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolDemo {
    public static void main(String[] args) {
        // 创建固定大小的线程池(3个线程)
        ExecutorService pool = Executors.newFixedThreadPool(3);

        // 提交5个任务(线程池会复用3个线程处理)
        for (int i = 0; i < 5; i++) {
            int taskId = i;
            pool.submit(() -> {
                System.out.println("处理任务" + taskId + ",线程:" + Thread.currentThread().getName());
            });
        }

        // 关闭线程池
        pool.shutdown();
    }
}

四、并发带来的问题及解决方案

并发虽提高效率,但多线程共享资源时会引发问题:

1. 线程安全问题

当多个线程同时操作共享数据(如全局变量、集合),可能导致数据不一致。
示例 :两个线程同时对变量 count 做 ++ 操作,预期结果为 2,实际可能为 1(因 ++ 是多步操作,可能被打断)。

2. 解决方案
  • synchronized 关键字:通过"锁"保证同一时间只有一个线程执行临界区代码(修饰方法或代码块)。

    java 复制代码
    public class SynchronizedDemo {
        private static int count = 0;
        private static final Object lock = new Object(); // 锁对象
    
        public static void main(String[] args) throws InterruptedException {
            Thread t1 = new Thread(() -> {
                for (int i = 0; i < 10000; i++) {
                    synchronized (lock) { // 同步代码块:同一时间只有一个线程进入
                        count++;
                    }
                }
            });
    
            Thread t2 = new Thread(() -> {
                for (int i = 0; i < 10000; i++) {
                    synchronized (lock) {
                        count++;
                    }
                }
            });
    
            t1.start();
            t2.start();
            t1.join(); // 等待线程执行完毕
            t2.join();
            System.out.println("count最终值:" + count); // 正确输出20000
        }
    }
  • java.util.concurrent 工具类 :提供线程安全的集合(如 ConcurrentHashMap)、原子类(如 AtomicInteger)、锁机制(如 ReentrantLock)等,比 synchronized 更灵活。

五、并发编程的核心挑战

  1. 可见性 :一个线程修改的共享变量,其他线程可能无法立即看到(因 CPU 缓存导致)。

    解决方案:使用 volatile 关键字(保证变量修改后立即刷新到主内存)。

  2. 原子性 :一个操作不可被中断(如 count++ 实际是"读-改-写"三步,非原子操作)。

    解决方案:synchronized、原子类(AtomicInteger)。

  3. 有序性 :CPU 可能对指令重排序优化,导致代码执行顺序与预期不一致。

    解决方案:volatile、synchronized 或显式内存屏障。

六、总结

  • 并发的本质:通过多线程交替执行,提高资源利用率和程序响应速度。
  • 核心问题:线程安全(数据不一致),需通过锁机制或并发工具解决。
  • 实践建议 :优先使用线程池管理线程,避免手动创建;复杂场景下借助 java.util.concurrent 包的工具类(如 CountDownLatch、Semaphore)简化开发。

理解并发是 Java 进阶的关键,尤其在高并发场景(如分布式系统、高流量服务器)中,合理设计并发模型能显著提升系统性能。

相关推荐
这个昵称不容易39 分钟前
SpringBoot「衣依」服装销售平台:144个Java文件180个接口,从挑衣服到收货评价整条链路都在
java·springboot·毕设
计算机毕设定制辅导-无忧学长1 小时前
《基于微信小程序的居家医疗陪护预约系统设计与实现》
java·spring boot·微信小程序·uniapp·居家医疗陪护预约系统
张张123y1 小时前
DSH 插件调试避坑:client.js 设置面板、热重载与高频报错排查表
开发语言·javascript·人工智能·开源·ecmascript·后端开发·ai agent
文丑颜不良啊2 小时前
LeetCode2221~2240题目解析
java·leetcode
秋饼2 小时前
Computer Use Agent Java 落地实战:感知-行动循环、执行器与安全围栏生产级实现
java·ai·技术分享·后端开发
冻柠檬飞冰走茶4 小时前
字母移动游戏题解:贪心求最长可保留连续子串(附 C 实现)
c语言·开发语言·数据结构·算法·动态规划
文丑颜不良啊4 小时前
LeetCode2161~2180题目解析
java·leetcode
FRing034 小时前
vue2干货总结7(自定义指令、插槽、单页应用程序、路由的使用(VueRouter))
开发语言·前端·vue.js·vue2
雪落漂泊4 小时前
C++算法 二分查找篇(上)
开发语言·c++·算法
波力海苔夹心脆6756 小时前
C# WPF 资源字典详解:ResourceDictionary、MergedDictionaries 与自定义控件案例
开发语言·c#·wpf·入门教程·resource·资源字典