面试直击:并发编程三要素+线程安全全攻略!

软件求生 2025-01-13 10:49:07

 Hi,大家好,我是小米,一个喜欢分享技术的程序员大哥哥!最近有个学弟来找我,说在面试某大厂Java岗位时被问了个问题:“并发编程三要素是什么?在Java中如何保证多线程运行的安全?”学弟被问懵了,不知道怎么答。没关系!今天咱们就来聊聊这个经典问题! 并发编程三要素是什么? 在多线程编程中,最重要的三大核心要素是 原子性、可见性 和 有序性。这三个概念是并发编程的基础,掌握它们可以帮助我们更好地理解线程安全问题。 1. 原子性(Atomicity) 原子性指的是一个操作要么完全执行,要么完全不执行,不会出现中间状态。 比如,银行转账时,假设 A 给 B 转100块,分两步: 从 A 的账户扣 100 块; 往 B 的账户加 100 块。 如果这两个操作中间被打断,导致只执行了第一个操作,钱就“丢”了。所以,我们需要保证整个转账操作的原子性。 在 Java 中,synchronized 和 Lock 是保证原子性的重要工具。 2. 可见性(Visibility) 可见性指的是当一个线程修改了共享变量的值,其他线程能够及时知道这个修改。 Java 中每个线程都有自己的工作内存,当线程 A 修改了一个变量,线程 B 并不知道,因为 B 可能还在用它的旧值。这种情况下就会发生数据不一致的问题。 为了解决可见性问题,Java 提供了 volatile 关键字,以及 synchronized 和 Lock 机制。 3. 有序性(Orderliness) 有序性指的是程序的执行顺序按照代码的书写顺序来运行。 不过,在并发编程中,编译器和处理器可能会为了优化性能而重排序,导致程序执行的顺序与代码顺序不同。 Java 提供了 volatile、synchronized 和 内存屏障 来避免重排序问题。 在 Java 中如何保证多线程运行安全? 多线程的安全问题可以概括为一句话:保证并发访问的共享资源在任何时候都是一致的。接下来,我们看看几种保证线程安全的方式。 1. 使用 synchronized synchronized 是 Java 提供的最基础的锁机制,用于保证同一时刻只有一个线程可以访问某段代码。比如:

上面的 withdraw 和 getBalance 方法都被 synchronized 修饰,保证了对 balance 的访问是线程安全的。 2. 使用 Lock Java 的 java.util.concurrent.locks 包提供了更灵活的锁机制,比如 ReentrantLock。它和 synchronized 的区别在于: 可以尝试加锁(tryLock); 可以定时加锁; 支持中断。 示例代码:

3. 使用 volatile 当我们只需要保证可见性而不是原子性时,可以使用 volatile。例如:

volatile 确保 running 的修改对其他线程是可见的。 4. 使用 原子类 Java 提供了一系列原子类(AtomicInteger, AtomicLong, 等),用于保证操作的原子性。例如:

这里的 incrementAndGet 方法是线程安全的。 5. 使用线程安全的集合 在并发环境下操作集合时,可以使用 java.util.concurrent 包下的线程安全集合类,比如: ConcurrentHashMap CopyOnWriteArrayList BlockingQueue 示例代码:

这些集合类在内部已经实现了线程安全机制,避免了手动加锁的麻烦。 总结 并发编程三要素: 原子性 - 保证操作不可分割。 可见性 - 保证线程间的修改是可见的。 有序性 - 保证代码按预期顺序执行。 多线程安全的解决方案: 使用 synchronized 或 Lock 来控制线程访问; 使用 volatile 解决可见性问题; 使用原子类处理简单的原子性操作; 使用线程安全的集合类简化开发。 END 希望通过这篇文章,大家能更好地理解并发编程三要素以及如何用 Java 编写线程安全的程序。如果觉得有收获,不要忘了点个赞或者转发给身边的朋友哦!有任何问题,也欢迎在评论区留言,我们一起探讨~ 我是小米,咱们下次见啦! 我是小米,一个喜欢分享技术的29岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号“软件求生”,获取更多技术干货!

0 阅读:1
软件求生

软件求生

从事软件开发,分享“技术”、“运营”、“产品”等。