如何理解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 可能对指令重排序优化,导致代码执行顺序与预期不一致。

    解决方案:volatilesynchronized 或显式内存屏障。

六、总结

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

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

相关推荐
冷雨夜中漫步几秒前
Claude Code源码分析——Claude Code Agent Loop 详细设计文档
java·开发语言·人工智能·ai
超龄编码人3 分钟前
Qt Widgets Designer QTabWidget无法添加布局
开发语言·qt
直奔標竿6 分钟前
Java开发者AI转型第二十六课!Spring AI 个人知识库实战(五)——联网搜索增强实战
java·开发语言·人工智能·spring boot·后端·spring
Python大数据分析@12 分钟前
CLI一键采集,使用Python搭建TikTok电商爬虫Agent
开发语言·爬虫·python
@小码农36 分钟前
2026年3月Scratch图形化编程等级考试一级真题试卷
开发语言·数据结构·c++·算法
这儿有一堆花37 分钟前
住宅代理(Residential Proxy)技术指南
开发语言·数据库·php
one_love_zfl1 小时前
java面试-微服务组件篇
java·微服务·面试
一只大袋鼠1 小时前
Java进阶:CGLIB动态代理解析
java·开发语言
秦ぅ时1 小时前
保姆级教程|OpenAI tts-1-hd模型调用全流程(Python+curl+懒人用法)
开发语言·python
Eiceblue1 小时前
使用 C# 将 Excel 转换为 Markdown 表格(含批量转换示例)
开发语言·c#·excel