【Java并发编程】并发:线程安全三要素及解决方案 你的名字 2022-11-02 13:22 133阅读 0赞 并发编程的本质其实就是利用多线程技术,在现代多核的CPU的背景下,催生了并发编程的趋势,通过并发编程的形式可以将多核CPU的计算能力发挥到极致,性能得到提升。除此之外,面对复杂业务模型,并行程序会比串行程序更适应业务需求,而并发编程更能吻合这种业务拆分 。 即使是单核处理器也支持多线程执行代码,CPU通过给每个线程分配CPU时间片来实现 这个机制。时间片是CPU分配给各个线程的时间,因为时间片非常短,所以CPU通过不停地切换线程执行,让我们感觉多个线程是同时执行的,时间片一般是几十毫秒(ms)。 并发不等于并行:并发指的是多个任务交替进行,而并行则是指真正意义上的“同时进行”。实际上,如果系统内只有一个CPU,而使用多线程时,那么真实系统环境下不能并行, 只能通过切换时间片的方式交替进行,而成为并发执行任务。真正的并行也只能出现在拥有多个CPU的系统中。 并发的优点: 1. 充分利用多核CPU的计算能力 2. 方便进行业务拆分,提升应用性能; 并发产生的问题: 1. 高并发场景下,导致频繁的上下文切换 2. 临界区线程安全问题,容易出现死锁的,产生死锁就会造成系统功能不可用 3. 其它 CPU通过时间片分配算法来循环执行任务,当前任务执行一个时间片后会切换到下一个任务。但是,在切换前会保存上一个任务的状态,以便下次切换回这个任务时,可以再加载这个任务的状态。所以任务从保存到再加载的过程就是一次上下文切换。 ![在这里插入图片描述][20210412221558207.png_pic_center] ### 1.可见性 ### 多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看到修改的值。 **解决方案** 1)JMM 提供了 volatile 2)synchronized(当程序除了要保证可见性,还要保证原子性时) ### 2.有序性 ### * 若在本线程内观察,所有操作是有有序的 * 若在一个线程观察另一个线程,所有操作时无序的 * 在 JVM 中,为了效率允许编译器和处理器对指令进行重排序 **解决方案** 1)Java内存模型具备一些先天的“有序性”,即不需要通过任何手段就能够得到保证的有序性 * as-if-seria(线程内) * 指令重排必须保证,单线程内重排序后执行结果不变。 * 为了遵守as-if-serial语义,编译器和处理器不会对存在数据依赖关系的操作做重排序,因为这种重排序会改变执行结果。但是,如果操作之间不存在数据依赖关系,这些操作就可能被编译器和处理器重排序。 * happens-before(多线程)(JDK5,JSR-133内存模型) * 程序顺序规则:一个线程中的每个操作,happens-before于该线程任意后续操作 * start()规则:线程的start()方法先于它的每一个动作,即如果线程A在执行线程B的 start方法之前修改了共享变量的值,那么当线程B执行start方法时,线程A对共享变量 的修改对线程B可见 * join()规则:Thread.join()方法的作用是等待当前执行的线程终止。假设在线程B终止之前,修改了共享变量,线程A从线程B的join方法成功返回后,线程B对共享变量的修改将对线程A可见。 * volatile变量规则:volatile变量的写,先发生于读,这保证了volatile变量的可见性,简单 的理解就是,volatile变量在每次被线程访问时,都强迫从主内存中读该变量的值,而当 该变量发生变化时,又会强迫将新的值刷新到主内存,任何时刻,不同的线程总是能 够看到该变量的新值 * 监视器锁规则:解锁(unlock)操作必然发生在后续的同一个锁的加锁(lock)之前,也就是说, 如果对于一个锁解锁后,再加锁,那么加锁的动作必须在解锁动作之后(同一个锁) * 传递性:如果A happens-before B,B happens-before C ,那么 A happens-before C * 线程中断规则:对线程 interrupt()方法的调用先行发生于被中断线程的代码检测到中 断事件的发生,可以通过Thread.interrupted()方法检测线程是否中断 * 对象终结规则:对象的构造函数执行,结束先于finalize()方法 2)还可以使用 volatile 和 synchronized 两个关键字来保证有序性(当默认的规则无法实现时,比如DCL单例) ### 3.原子性 ### 一个线程执行一段代码时不被打断,要么都成功,要么都失败 > 注意:我们可以大致认为基本类型变量的读写是具备原子性的 **解决方案** 1)悲观锁:synchronized 关键字,JVM 级别锁 > 注意:由于JMM 定义的 lock 相关规则,synchronized 除了保证原子性还能保证可见性和有序性 > > * 可见性:对一个变量执行unlock操作之前,必须先把此变量同步回主内存中,即执行store和write操作 > * 有序性:一个变量同一时刻只允许一条线程对其进行lock操作 2)乐观锁:自旋+CAS(比如 AtomicInteger 的自增操作) > 注意:这种方案无法保证可见性,一般配合 volatile 使用。 [20210412221558207.png_pic_center]: /images/20221024/80232df9b112466f889041f9647a6a54.png
相关 Java并发编程中,线程安全问题及解决方案实例 在Java并发编程中,线程安全问题主要体现在以下几点: 1. 同一时刻多个线程同时访问和修改共享数据。 2. 不正确的同步机制导致了竞态条件。 解决线程安全问题的常见策略包 待我称王封你为后i/ 2024年09月18日 17:33/ 0 赞/ 11 阅读
相关 Java多线程并发编程问题及解决方案 在Java多线程并发编程中,常见的问题包括但不限于: 1. **数据不一致(race condition)**:多个线程同时访问共享资源,可能会导致结果错误。 2. **死 清疚/ 2024年09月17日 09:45/ 0 赞/ 22 阅读
相关 Java并发编程:线程安全问题分析及解决方案 在Java中,线程安全是一个重要的话题。它通常涉及到多个线程同时访问和修改共享数据的问题。 线程不安全的表现: 1. 无锁竞态(Race Condition):两个或更多的 Dear 丶/ 2024年09月16日 18:58/ 0 赞/ 20 阅读
相关 Java并发编程:线程安全方法及实例 在Java中,线程安全(thread-safe)是指一个方法或代码块在多线程环境下执行时不会导致数据的不一致。以下是一些线程安全的方法和实例: 1. 同步块/方法(synch 待我称王封你为后i/ 2024年09月16日 06:03/ 0 赞/ 10 阅读
相关 Java多线程并发编程问题及解决方案示例 在Java多线程并发编程中,可能会遇到各种问题,如死锁、资源泄露、同步问题等。这里提供几个常见问题的解决示例。 1. **死锁**:两个或多个线程相互等待对方释放资源而造成的 灰太狼/ 2024年09月13日 11:57/ 0 赞/ 24 阅读
相关 并发编程:Java多线程常见问题及解决方案 在Java多线程编程中,常见的问题和解决方案包括: 1. **死锁**:多个线程等待对方释放资源而无法继续。解决方法是使用超时机制、设置固定资源顺序或使用死锁预防算法。 2 小灰灰/ 2024年09月05日 15:30/ 0 赞/ 21 阅读
相关 【Java并发编程】并发:线程安全三要素及解决方案 并发编程的本质其实就是利用多线程技术,在现代多核的CPU的背景下,催生了并发编程的趋势,通过并发编程的形式可以将多核CPU的计算能力发挥到极致,性能得到提升。除此之外,面对复杂 你的名字/ 2022年11月02日 13:22/ 0 赞/ 134 阅读
相关 并发编程-线程安全 1. 什么是线程安全 在多线程访问下, 不管哪个线程调用这个类,这个类总能表现出正确的行为 JMM 引发的问题 ![在这里插入图片描述][watermark_ty ゝ一纸荒年。/ 2022年04月24日 05:28/ 0 赞/ 271 阅读
相关 并发编程-线程安全 原因:Java内存模型 共享内存模型指的就是Java内存模型(简称JMM),JMM决定一个线程对共享变量的写入时,能对另一个线程可见。线程之间的共享变量存储 ゝ一纸荒年。/ 2022年04月23日 18:36/ 0 赞/ 224 阅读
还没有评论,来说两句吧...