Java内存模型JMM学习之上下文切换-多线程一定快吗?

写在前面:本系列博客主要记录自己的学习过程,回顾JMM相关的东西,主要参考《java并发编程的艺术》,这本书粗略的看了两遍,但是里面很多东西很模糊,决定再重新捋一遍,理解了才是自己的。

一个概念

时间片:Cpu分配给各个线程的时间(非常短)。

多线程怎么工作?

       就算是单核处理器也支持多线程,cpu通过给每个线程分配时间片来实现这个机制。,cpu通过不断的切换线程执行,由于比较快,所以我们感觉多个线程是同时执行。

带来性能的影响

上线文切换会影响线程执行的速度。

一个问题,多线程一定快吗?

经过测试给出的答案是:不一定。当并发执行累加操作不超过百万次的时候,速度会比串行执行累加操作要慢。因为线程创建存在一个上线文切换的开销。

如何减少上下文开销?

无锁并发编程,CAS算法,使用最少线程和使用协程。

无锁并发编程:开发用一些方法避免使用锁,使用特定的算法,不同的线程处理不同的数据

CAS:Java包使用CAS算法,一种基于乐观锁的算法,javaAutomic包使用了CAS算法更新数据,不需要加锁。

使用最少线程:避免创建不需要的线程,比如任务很少,但是创建了很多线程来处理,这样会造成大量线程都处于等待状态。

协程:单线程里实现任务的调度,并在单线程里维持多个任务间的切换。

资源限制的考虑:程序的执行顺序受限于计算机硬件资源或者软件资源。

 

      并发执行代码执行的速度和加快的原则将代码中串行执行的部分变成并发执行,受限于资源,仍然在串行执行(个人理解,多个线程并发执行资源就一个,比如一个管道,他们同时处理,可以把同时处理看成一个串行的)由于资源限制,这种运行速度肯定变慢,反倒不如单线程执行的快了,严重的时候cpu利用率会达到100%,会导致很长时间不能完成任务。改成单线程一个小时就好了。

如何解决?

硬件方面

集群!比如使用ODPS,Hadoop或者自己搭建的集群,不同的机器处理不同的数据。

软件方面

    资源复用。比如使用连接池,Socket连接复用或者调用对方webService接口获取数据时只建立一个连接(个人理解也就是短连接)。

### Java JMM内存模型相关面试题解释 #### 什么是Java内存模型JMM)? Java内存模型Java Memory Model, JMM)是一种规范,定义了多线程环境下变量的访问规则以及线程间通信的原则[^3]。它描述了主内存与工作内存之间的关系,并规定了线程如何读写共享变量以确保数据一致性。 #### JMM的主要目标是什么? JMM的目标是屏蔽底层硬件和操作系统的差异,为开发者提供统一的内存访问视图,从而保证Java程序在不同平台上具有一致的并发行为[^2]。 #### JMM的核心概念有哪些? 1. **主内存(Main Memory)** 所有线程共享的内存区域,存储了实例字段、静态字段和数组元素等变量。它是所有对象和变量的最终存储位置[^5]。 2. **工作内存(Working Memory/Thread Local Memory)** 每个线程都有自己独立的工作内存,其中保存了从主内存复制过来的变量副本。线程对变量的操作均在其工作内存中进行,随后再同步回主内存[^5]。 3. **原子性(Atomicity)** 原子性指的是某个操作不可被中断,要么全部执行成功,要么完全不执行。大多数基本数据类型的读写操作具有原子性,但对于`long`和`double`类型,在某些情况下可能存在非原子性问题。复杂操作通常需要借助`synchronized`或显式的锁机制来保证原子性。 4. **可见性(Visibility)** 当一个线程修改了共享变量的值时,其他线程能够及时感知这一变化。`volatile`关键字可以通过禁止指令重排序并强制每次访问都从主内存加载最新值的方式,确保变量的可见性。 5. **有序性(Ordering)** 即使代码按顺序书写,但由于编译器优化或处理器乱序执行等原因,实际运行过程中可能出现指令重排现象。JMM通过`happens-before`原则约束特定操作间的相对顺序,防止因重排引发错误。 #### 如何解决“内存可见性”问题? 内存可见性问题是由于缓存的存在而导致的一个线程无法立刻获取另一个线程所作更改的情况。以下是几种常见的解决方案: - 使用`volatile`修饰符:它可以阻止指令重排,同时确保每次使用该变量都会直接从主内存读取其当前值[^4]。 - 利用`synchronized`关键字:进入同步块之前会清空本地缓存并将修改后的数据刷新至主内存;退出时也会重新载入最新的全局状态[^5]。 - 显式调用内存屏障(Memory Barriers/Fences):这类低级工具能有效控制何时停止进一步处理直到先前所有的改动都被提交完毕为止。 #### 示例代码展示 下面给出一段利用`volatile`实现简单计数功能的例子: ```java public class VolatileExample { private static volatile int count = 0; public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(() -> { for (int i = 0; i < 1000; ++i) { increment(); } }); Thread t2 = new Thread(() -> { while (count < 1000) {} System.out.println("Count reached 1000"); }); t1.start(); t2.start(); t1.join(); // Wait until thread finishes execution. } private static synchronized void increment() { // Synchronization ensures atomicity here. count++; } } ``` 此例子展示了两个线程分别负责增加计数值与监控进度的任务流程。这里采用`synchronized`方法不仅实现了增量动作本身的独占权管理还间接促进了跨进程间消息传递效率提升因为锁释放前必然伴随一次完整的上下文切换过程包括但不限于清理临时寄存单元内容物并向外部暴露更新成果等等一系列连锁反应共同构成了整个事务链条不可或缺的重要环节之一[^5]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值