并发

并发编程又称为多线程编程,包括同步:线程之间协作;互斥:独占锁 ; 分工:大任务拆解。

并发特性:原子性、可见性、有序性

java线程为内核级线程,jvm不具备直接调度CPU的权限,因为Thread类的start等方法内调用的都是native方法,native方法底层是c++实现的。

Java Thread依赖OSThread,由OSThread创建内核线程(pthread(库)的create方法),所以线程创建、切换、销毁等是由用户态与内核态之间切换,需要额外开销。为此提出了线程池,提高线程复用,减少资源消耗。

并发的风险:

1.性能问题:线程上下文切换

2.线程安全问题

3.活跃性问题:饥饿、死锁、活锁

活锁:任务或者执行者没有被阻塞,由于某些条件没有满足,导致一直重复尝试、失败、尝试、失败。在这期间线程状态会不停的改变。

饥饿:【即线程一直在等待却无法执行的原因】

1.高优先级线程抢占资源线程
2.在等待一个本身也处于永久等待完成的对象
3.线程被永久阻塞在一个等待进入同步块的状态,因为其他线程总是能在他之前持续地对该同步块进行访问(比如阻塞在synchronized)

lock的作用:保证后续指令的原子性,禁止指令重排,把线程工作内存中所有数据刷新到主内存中。(原子性、有序性、可见性)

一、JMM模型

JMM为java线程内存模型,围绕着在并发过程中如何处理可见性、原子性、有序性这三个特性而建立的模型。

JMM模型主要定义主内存和线程工作内存之间如何交互,且屏蔽了不同操作系统底层访问差异。

JMM规定了所有的变量都存储在主内存(Main Memory)中。每个线程还有自己的工作内存(Working Memory),线程的工作内存中保存了该线程使用到的变量的主内存的副本拷贝,线程对变量的所有操作(读取、赋值等)都必须在工作内存中进行,而不能直接读写主内存中的变量(volatile变量仍然有工作内存的拷贝,但是由于它特殊的操作顺序性规定,所以看起来如同直接在主内存中读写访问一般)。不同的线程之间也无法直接访问对方工作内存中的变量,线程之间值的传递都需要通过主内存来完成。

 

二、volatile 

Java 语言提供了一种稍弱的同步机制,即 volatile 变量,用来确保将变量的更新操作通知到其他线程。volatile 变量具备两种特性,volatile 变量不会被缓存在寄存器或者对其他处理器不可见的 地方,因此在读取 volatile 类型的变量时总会返回最新写入的值。

当对非 volatile 变量进行读写的时候,每个线程先从主内存拷贝变量到 CPU 缓存(线程工作内存)中。如果计算机有 多个 CPU,每个线程可能在不同的 CPU 上被处理,这意味着每个线程可以拷贝到不同的 CPU cache 中。而声明变量是 volatile 的,JVM 保证了每次读变量都从主内存中读,跳过 CPU cache 这一步。

 

两种特性:变量可见性、禁止指令重排。

 

 

volatile 适合场景:一个变量被多个线程共享,线程直接给这个变量赋值。 

如何在两个线程之间共享数据?

Java 里面进行多线程通信的主要方式就是共享内存的方式,共享内存主要的关注点有两个:可见性和有序性原子性。Java 内存模型(JMM)解决了可见性和有序性的问题,而锁解决了原子性的问题,理想情况下我们希望做到“同步”和“互斥”。有以下常规实现方法:

多个线程共享数据分两种情况:

1、如果多个线程执行同一个Runnable实现类中的代码,此时共享的数据放在Runnable实现类中;

2、如果多个线程执行不同的Runnable实现类中的代码,此时共享数据和操作共享数据的方法封装到一个对象中,在不同的Runnable实现类中调用操作共享数据的方法。

 

posted @   堤苏白  阅读(163)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 字符编码:从基础到乱码解决
· 提示词工程——AI应用必不可少的技术
点击右上角即可分享
微信分享提示