【高薪程序员必看】万字长文拆解Java并发编程!(3-1):并发共享问题的解决与分析

简介: 活锁:多个线程相互影响对方退出同步代码块的条件而导致线程一直运行的情况。例如,线程1的退出条件是count=5,而线程2和线程3在其代码块中不断地是count进行自增自减的操作,导致线程1永远运行。内存一致性问题:由于JIT即时编译器对缓存的优化和指令重排等造成的内存可见性和有序性问题,可以通过synchronized,volatile,并发集合类等机制来解决。这里的线程安全是指,多个线程调用它们同一个实例的方法时,是线程安全的,但仅仅能保证当前调用的方法是线程安全的,不同方法之间是线程不安全的。

目录


3. 并发共享

3.1. 并发问题

3.1.1. 临界区

3.1.2. 竞态条件

3.1.3. 死锁

3.1.4. 活锁

3.1.5. 饥饿

3.1.6. 内存一致性

3.2. 线程共享问题解决

3.3. 变量的线程安全分析

3.3.1. 成员变量和静态变量

3.3.2. 局部变量

3.4. 常见线程安全类



3. 并发共享

3.1. 并发问题

多个线程同时对共享资源进行修改时,有可能会发生读写操作的指令交错,导致结果跟预期不一样

3.1.1. 临界区

临界区(critical region):一段对共享资源的多线程并发读写操作的代码块

3.1.2. 竞态条件

竞态条件(Race Condition):多个线程代码在临界区执行,由于执行顺序不同而导致结果无法预测,例如数据丢失不一致等

解决方法:使用锁,同步工具,原子类等机制

3.1.3. 死锁

死锁的概念:多个线程由于资源竞争或通信问题导致的阻塞情况。例如:线程1持有线程2所需的资源,且线程1不释放,那么线程2就永远阻塞。

死锁的预防:

  1. 破坏互斥条件,允许资源共享
  2. 请求不到资源时,释放自己持有的资源
  3. 按序获取资源,避免循环等待

死锁的解决:

  1. 杀死持有资源的进程
  2. 操作系统强制释放资源
  3. 回滚进程

3.1.4. 活锁

活锁:多个线程相互影响对方退出同步代码块的条件而导致线程一直运行的情况。例如,线程1的退出条件是count=5,而线程2和线程3在其代码块中不断地是count进行自增自减的操作,导致线程1永远运行。

活锁的解决:

  1. 引入随机性,在检测到活锁时,暂停随机时间再重新尝试
  2. 设置重试次数,到达一定次数强制退出
  3. 活锁发生时主动干预,打破活锁

3.1.5. 饥饿

饥饿(Starvation):某个线程长期无法获取CPU时间片执行权而无法运行的情况,一般发生在字段调用不公平或优先级设置有问题的情况下

3.1.6. 内存一致性

内存一致性问题:由于JIT即时编译器对缓存的优化和指令重排等造成的内存可见性和有序性问题,可以通过synchronized,volatile,并发集合类等机制来解决

3.2. 线程共享问题解决

有多种方法可以避免临界区中的竞态条件:

  • 阻塞式-悲观锁:synchronized,Lock
  • 非阻塞式-乐观锁:原子变量

3.3. 变量的线程安全分析

3.3.1. 成员变量和静态变量

成员变量和静态变量的线程安全分析要分两种情况:

  • 不被共享时是线程安全的
  • 被多个线程共享时,分为两种情况
  • 读操作:是线程安全的
  • 写操作:是线程不安全的

3.3.2. 局部变量

局部变量是否线程安全在于它是否逃逸出方法的作用范围

  • 如果局部变量作用范围仅仅在当前方法,是线程安全的
  • 如果局部变量作为参数传入或作为返回值返回,说明局部变量没有逃逸出方法的作用范围,是线程不安全的
  • 局部变量仅有一个线程访问的时候是线程安全的
  • private和final关键字是可以提供线程安全的,因为可以防止子类重写父类方法,其中可以开启另外一个线程访问局部变量造成线程不安全

3.4. 常见线程安全类

不可变类:内部属性是不可改变的,只能读不能写,所以是线程安全的

  • String:
  • String用final关键字修饰是为了防止子类去重写String方法导致线程不安全
  • 其中的replace,substring改变了值,本质上是通过创建一个新的String对象来完成的,线程安全
  • Integer

方法加上了synchronized方法的类:

  • StringBuffer
  • Random
  • Vector
  • Hashtable
  • java.util.concurrent包下的类,也称JUC

这里的线程安全是指,多个线程调用它们同一个实例的方法时,是线程安全的,但仅仅能保证当前调用的方法是线程安全的,不同方法之间是线程不安全的

相关文章
|
2月前
|
IDE Java 编译器
java编程最基础学习
Java入门需掌握:环境搭建、基础语法、面向对象、数组集合与异常处理。通过实践编写简单程序,逐步深入学习,打牢编程基础。
226 1
|
2月前
|
Java
如何在Java中进行多线程编程
Java多线程编程常用方式包括:继承Thread类、实现Runnable接口、Callable接口(可返回结果)及使用线程池。推荐线程池以提升性能,避免频繁创建线程。结合同步与通信机制,可有效管理并发任务。
165 6
|
3月前
|
SQL Java 数据库
2025 年 Java 从零基础小白到编程高手的详细学习路线攻略
2025年Java学习路线涵盖基础语法、面向对象、数据库、JavaWeb、Spring全家桶、分布式、云原生与高并发技术,结合实战项目与源码分析,助力零基础学员系统掌握Java开发技能,从入门到精通,全面提升竞争力,顺利进阶编程高手。
692 1
|
2月前
|
安全 前端开发 Java
从反射到方法句柄:深入探索Java动态编程的终极解决方案
从反射到方法句柄,Java 动态编程不断演进。方法句柄以强类型、低开销、易优化的特性,解决反射性能差、类型弱、安全性低等问题,结合 `invokedynamic` 成为支撑 Lambda 与动态语言的终极方案。
164 0
|
3月前
|
Java 开发者
Java并发编程:CountDownLatch实战解析
Java并发编程:CountDownLatch实战解析
463 100
|
2月前
|
Java 大数据 Go
从混沌到秩序:Java共享内存模型如何通过显式约束驯服并发?
并发编程旨在混乱中建立秩序。本文对比Java共享内存模型与Golang消息传递模型,剖析显式同步与隐式因果的哲学差异,揭示happens-before等机制如何保障内存可见性与数据一致性,展现两大范式的深层分野。(238字)
89 4
|
2月前
|
缓存 安全 Java
如何理解Java中的并发?
Java并发指多任务交替执行,提升资源利用率与响应速度。通过线程实现,涉及线程安全、可见性、原子性等问题,需用synchronized、volatile、线程池及并发工具类解决,是高并发系统开发的关键基础。(238字)
239 4
|
3月前
|
算法 Java
Java多线程编程:实现线程间数据共享机制
以上就是Java中几种主要处理多线程序列化资源以及协调各自独立运行但需相互配合以完成任务threads 的技术手段与策略。正确应用上述技术将大大增强你程序稳定性与效率同时也降低bug出现率因此深刻理解每项技术背后理论至关重要.
267 16
|
3月前
|
NoSQL Java 关系型数据库
超全 Java 学习路线,帮你系统掌握编程的超详细 Java 学习路线
本文为超全Java学习路线,涵盖基础语法、面向对象编程、数据结构与算法、多线程、JVM原理、主流框架(如Spring Boot)、数据库(MySQL、Redis)及项目实战等内容,助力从零基础到企业级开发高手的进阶之路。
340 1
|
7月前
|
机器学习/深度学习 消息中间件 存储
【高薪程序员必看】万字长文拆解Java并发编程!(9-2):并发工具-线程池
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发编程中的强力并发工具-线程池,废话不多说让我们直接开始。
281 0