Java内存模型与线程

简介: 本文主要内容:主内存与工作内存、volatile关键字、线程状态转换

本文主要内容
主内存与工作内存
volatile关键字
线程状态转换

与线程相关的内容,本博已经说过不少,本文着重阐述以前没有提及的内容

主内存与工作内存
在物理机上,“高效并发”并没有那么容易实现。因为任务不可能只依赖于处理器计算完成,至少与内存的交互很难消除。而内存设备与处理器的运算速度之间有着几个数据级的差距,所以现代计算机都添加高速缓存作为内存和处理器之间的缓冲。
内存将数据复制到缓存当中,当运算结束后再从缓存中同步回内存中。

值得一提的是,为了使处理器的运算单元被充分利用,处理器可能会对输入代码进行乱序执行,不过处理器会保证该结果与顺序执行的结果是一致的。

Java虚拟机为了屏蔽硬件差异,也设计了一套内存模型。
主内存:Java模型规定了所有的变量都存储在主内存中(与物理机中的内存对应,但仅是虚拟机内存的一部分)

工作内存:每条线程有自己的工作内存(可以物理机中的高速缓存类比),工作线程保存了变量的主内存副本拷贝

线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存的变量,不同线程间也无法直接访问对方工作内存中的变量,线程间变量值的传递均需要通过主内存来完成

Java内存模型定义了以下八种操作:

lock(锁定):作用于主内存变量,它把一个变量标识为一条线程独占状态

unlock(解锁):作用于主内存变量,释放处于锁定状态的变量,释放后的变量才可被其它线程使用

read(读取):作用于主内存的变量,它把变量值从主内存传输到工作内存中

load(载入):作用于工作内存的变量,它把read操作的变量值放入工作内存的副本中

use(使用):作用于工作内存的变量,它把工作内存中变量的值传递给执行引擎

assign(赋值):作用于工作内存的变量,它把从执行引擎收到的值赋值给工作内存的变量

store(存储):作用于工作内存变量,它把工作内存中变量值传递到主内存中

write(写入):作用于主内存的变量,它把store操作从工作内存中得到的变量值放入主内存中

volatile关键字

当一个变量被定义成volatile时,它具备两个特性,第1是保证此变量对所有线程的可见性(这里的可见性是指当一条线程修改了这个变量的值,新值对于其它线程立即可见,普通变量不行,因为要通过主内存才能知道),第2是禁止指令重排序优化,意思就是代码执行顺序和代码本身顺序一致。

虽然volatile变量可保证可见性,但它并不一定都是并发安全的。

public static volatile int race = 0;
public static void increase(){
    race++;
}
public static void main(String[] args) {
    Thread[] threads = new Thread[20];
    for (int i = 0; i < threads.length; i++) {
        threads[i] = new Thread(){
            public void run() {
                for (int j = 0; j < 10000; j++) {
                    increase();
                }
            }
        };
        threads[i].start();
    }
    while (Thread.activeCount() > 1) {
        Thread.yield();
    }
    System.out.println(race);
}

上述代码运行结果始终少于200000,得不到正确的运行结果。

我们直接查看javap内容,有助于理解

当getstatic指令把race值取到栈顶时,volatile关键字保证了race值此时是正确的,但在执行iconst_1、add这些指令的时候,其它线程可能已经把race值加大了,而在操作栈顶的值就变成了过期的数据,所以putstatic指令执行后就可能把较小的race值同步回主内存当中了。

volatile在如下两种情况下适合使用:

运算结果并不依赖变量的当前值,或者能确保只有单一线程修改变量的值
变量不需要与其它的状态变量共同参与不变约束

线程状态转换

Java中一共定义了5种线程状态:

新建(new):创建后尚未启动的线程处理这种状态
运行(runnable):runnable包括了操作线程线程状态中的Running和Ready,也就是处理此状态的线程有可能正在执行,也有可能正在等待CPU为它分配执行时间

无限期等待(waiting):处理这种状态的线程不会被分配CPU执行时间,它们要等待被其它线程显示地唤醒,没有设置timeout参数的 Object.wait()和Object.join()方法,会让线程陷入无限等待状态

期限等待(timed waiting):处理这种状态的线程不会被分配CPU执行时间,不过无须等待其它线程唤醒,一定时间后它们会由系统自动唤醒

阻塞(blocked):线程被阻塞,阻塞和等待的区别是:阻塞状态在等待着获取到一个排它锁,这个事件在另外一个线程放弃这外锁的时候发生。而等待状态则是在等待一段时间,或者唤醒动作的发生。

结束:已终止线程的状态,线程已经执行结束

作者:某昆
文章来源:https://www.jianshu.com/p/4b186593991a
更多参考内容:http://www.roncoo.com/article/index

相关文章
|
12天前
|
存储 Java 编译器
Java内存区域详解
Java内存区域详解
27 0
Java内存区域详解
|
13天前
|
安全 算法 Java
深入理解Java并发编程:线程安全与性能优化
【4月更文挑战第11天】 在Java中,高效的并发编程是提升应用性能和响应能力的关键。本文将探讨Java并发的核心概念,包括线程安全、锁机制、线程池以及并发集合等,同时提供实用的编程技巧和最佳实践,帮助开发者在保证线程安全的前提下,优化程序性能。我们将通过分析常见的并发问题,如竞态条件、死锁,以及如何利用现代Java并发工具来避免这些问题,从而构建更加健壮和高效的多线程应用程序。
|
1天前
|
存储 安全 Java
滚雪球学Java(19):JavaSE中的内存管理:你所不知道的秘密
【4月更文挑战第8天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
17 4
滚雪球学Java(19):JavaSE中的内存管理:你所不知道的秘密
|
1天前
|
安全 Java 调度
Java线程:深入理解与实战应用
Java线程:深入理解与实战应用
13 0
|
1天前
|
Java
Java中的并发编程:理解和应用线程池
【4月更文挑战第23天】在现代的Java应用程序中,性能和资源的有效利用已经成为了一个重要的考量因素。并发编程是提高应用程序性能的关键手段之一,而线程池则是实现高效并发的重要工具。本文将深入探讨Java中的线程池,包括其基本原理、优势、以及如何在实际开发中有效地使用线程池。我们将通过实例和代码片段,帮助读者理解线程池的概念,并学习如何在Java应用中合理地使用线程池。
|
5天前
|
安全 Java
深入理解 Java 多线程和并发工具类
【4月更文挑战第19天】本文探讨了Java多线程和并发工具类在实现高性能应用程序中的关键作用。通过继承`Thread`或实现`Runnable`创建线程,利用`Executors`管理线程池,以及使用`Semaphore`、`CountDownLatch`和`CyclicBarrier`进行线程同步。保证线程安全、实现线程协作和性能调优(如设置线程池大小、避免不必要同步)是重要环节。理解并恰当运用这些工具能提升程序效率和可靠性。
|
6天前
|
安全 Java
java多线程(一)(火车售票)
java多线程(一)(火车售票)
|
6天前
|
安全 Java 调度
Java并发编程:深入理解线程与锁
【4月更文挑战第18天】本文探讨了Java中的线程和锁机制,包括线程的创建(通过Thread类、Runnable接口或Callable/Future)及其生命周期。Java提供多种锁机制,如`synchronized`关键字、ReentrantLock和ReadWriteLock,以确保并发访问共享资源的安全。此外,文章还介绍了高级并发工具,如Semaphore(控制并发线程数)、CountDownLatch(线程间等待)和CyclicBarrier(同步多个线程)。掌握这些知识对于编写高效、正确的并发程序至关重要。
|
6天前
|
安全 Java 程序员
Java中的多线程并发编程实践
【4月更文挑战第18天】在现代软件开发中,为了提高程序性能和响应速度,经常需要利用多线程技术来实现并发执行。本文将深入探讨Java语言中的多线程机制,包括线程的创建、启动、同步以及线程池的使用等关键技术点。我们将通过具体代码实例,分析多线程编程的优势与挑战,并提出一系列优化策略来确保多线程环境下的程序稳定性和性能。
|
7天前
|
缓存 分布式计算 监控
Java并发编程:深入理解线程池
【4月更文挑战第17天】在Java并发编程中,线程池是一种非常重要的技术,它可以有效地管理和控制线程的执行,提高系统的性能和稳定性。本文将深入探讨Java线程池的工作原理,使用方法以及在实际开发中的应用场景,帮助读者更好地理解和使用Java线程池。