并发编程学习笔记

举报
一颗小谷粒 发表于 2025/03/31 17:18:20 2025/03/31
【摘要】 一、并发编程是什么并发编程,简单来说,就是在同一时间段内,程序可以执行多个任务。与顺序编程不同,并发编程允许程序中的多个部分同时推进,充分利用计算机的多核资源,提高系统的整体性能。在现实生活中,我们可以将并发类比为餐厅的服务流程:厨师在烹饪食物的同时,服务员可以为顾客点菜、上菜,多个任务同时进行,提高餐厅的运营效率。在计算机领域,并发编程涵盖了多线程、多进程、异步 I/O 等多种技术。其中,...

一、并发编程是什么

并发编程,简单来说,就是在同一时间段内,程序可以执行多个任务。与顺序编程不同,并发编程允许程序中的多个部分同时推进,充分利用计算机的多核资源,提高系统的整体性能。在现实生活中,我们可以将并发类比为餐厅的服务流程:厨师在烹饪食物的同时,服务员可以为顾客点菜、上菜,多个任务同时进行,提高餐厅的运营效率。
在计算机领域,并发编程涵盖了多线程、多进程、异步 I/O 等多种技术。其中,多线程编程是最常见的并发编程方式之一。线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。一个进程可以包含多个线程,这些线程共享进程的资源,如内存空间、文件描述符等,通过并发执行,提高程序的执行效率。

二、为什么需要并发编程

1. 提升性能

在单核处理器时代,程序通过分时复用的方式模拟并发执行,虽然在宏观上多个任务似乎在同时进行,但在微观上,处理器同一时刻只能执行一个任务。而多核处理器的出现,为真正的并发执行提供了硬件基础。通过并发编程,将多个任务分配到不同的核心上执行,可以充分利用多核处理器的计算能力,大大缩短程序的执行时间。

2. 增强响应性

在交互式应用中,如桌面应用、Web 应用等,用户希望系统能够实时响应用户的操作。如果采用顺序编程,当一个任务执行时间较长时,整个应用将处于阻塞状态,无法响应用户的其他操作。而并发编程可以将耗时的任务放到后台线程中执行,主线程继续响应用户的输入,提高应用的响应性和用户体验。

3. 充分利用资源

在一些场景中,如网络通信、文件 I/O 等,操作往往需要等待外部设备的响应,这段时间内处理器处于空闲状态。通过并发编程,可以在等待的同时执行其他任务,充分利用处理器资源,提高系统的整体利用率。

三、并发编程的常见问题与解决方案

1. 线程安全问题

当多个线程同时访问共享资源时,可能会导致数据不一致或其他错误,这就是线程安全问题。例如,多个线程同时对一个计数器进行加 1 操作,由于线程切换的不确定性,可能会导致最终的结果与预期不符。
为了解决线程安全问题,常见的方法有同步机制和锁机制。在 Java 中,可以使用synchronized关键字来实现同步,确保同一时刻只有一个线程能够访问共享资源。例如:
class Counter {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public int getCount() {
        return count;
    }
}
 
此外,Java 还提供了java.util.concurrent.atomic包,其中的原子类如AtomicInteger、AtomicLong等,通过硬件级别的原子操作,实现了无锁的线程安全。

2. 死锁问题

死锁是并发编程中另一个常见的问题,当两个或多个线程相互等待对方释放资源时,就会发生死锁。例如,线程 A 持有资源 1,等待资源 2;线程 B 持有资源 2,等待资源 1,此时两个线程都无法继续执行,形成死锁。
为了避免死锁,可以采用以下策略:
  • 避免嵌套锁:尽量减少锁的嵌套使用,降低死锁发生的可能性。
  • 设置锁超时:为锁设置超时时间,当等待锁的时间超过设定值时,放弃等待,释放已持有的资源。
  • 使用资源排序:对资源进行排序,所有线程按照相同的顺序获取资源,避免资源获取顺序不一致导致的死锁。

3. 性能问题

虽然并发编程可以提高性能,但如果使用不当,反而会导致性能下降。例如,过多的线程切换会消耗大量的 CPU 资源,锁的竞争会导致线程阻塞,降低系统的并发度。
为了优化并发性能,可以采用以下方法:
  • 线程池:使用线程池来管理线程,避免频繁创建和销毁线程,降低线程创建和销毁的开销。
  • 减少锁的粒度:尽量缩小锁的保护范围,只对必要的资源进行加锁,降低锁的竞争程度。
  • 无锁编程:采用无锁数据结构和算法,如 CAS(Compare - And - Swap)操作,避免锁带来的性能开销。

四、并发编程实践:Java 并发包的应用

Java 提供了丰富的并发编程工具,其中java.util.concurrent包(简称 JUC 包)是 Java 并发编程的核心。下面通过几个示例,介绍 JUC 包中常用类的使用。

1. 线程池的使用

线程池可以有效地管理线程,提高线程的复用率。在 Java 中,可以使用ThreadPoolExecutor类来创建线程池。例如:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class ThreadPoolExample {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 5; i++) {
            final int task = i;
            executorService.submit(() -> {
                System.out.println(Thread.currentThread().getName() + " is processing task " + task);
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
        executorService.shutdown();
    }
}

2. 并发集合的使用

JUC 包提供了一系列线程安全的并发集合,如ConcurrentHashMap、CopyOnWriteArrayList等。这些集合在多线程环境下具有更好的性能和安全性。例如:
import java.util.concurrent.ConcurrentHashMap;

public class ConcurrentHashMapExample {
    public static void main(String[] args) {
        ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
        map.put("key1", 1);
        map.put("key2", 2);
        System.out.println(map.get("key1"));
    }
}

3. 同步工具类的使用

JUC 包还提供了一些同步工具类,如CountDownLatch、CyclicBarrier等,用于协调多个线程之间的执行。例如,CountDownLatch可以让一个线程等待其他多个线程完成任务后再继续执行:
import java.util.concurrent.CountDownLatch;

public class CountDownLatchExample {
    public static void main(String[] args) throws InterruptedException {
        int n = 3;
        CountDownLatch latch = new CountDownLatch(n);
        for (int i = 0; i < n; i++) {
            new Thread(() -> {
                try {
                    System.out.println(Thread.currentThread().getName() + " is working");
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    latch.countDown();
                }
            }).start();
        }
        latch.await();
        System.out.println("All tasks are completed");
    }
}

五、总结

并发编程是一项复杂而又强大的技术,它为我们提供了提升软件性能和响应性的有效途径。通过本文的介绍,相信你对并发编程有了更深入的理解,包括其基本概念、常见问题及解决方案,以及在 Java 中的实践应用。
在实际开发中,要根据具体的业务需求和场景,合理运用并发编程技术。同时,不断积累经验,深入理解并发编程的原理和机制,才能编写出高效、稳定的并发程序。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。