7张图带你轻松理解Java 线程安全,java缓存机制面试

简介: 7张图带你轻松理解Java 线程安全,java缓存机制面试

e8312a5bdb3649a0d58dc422044b1df8_aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy8xNTY3OTEwOC1jNzc1NjE3NTJjOWQ1NDdi.jpg

上图描述了一个多线程执行场景。线程 A 和线程 B 分别对主内存的变量进行读写操作。其中主内存中的变量为共享变量,也就是说此变量只此一份,多个线程间共享。但是线程不能直接读写主内存的共享变量,每个线程都有自己的工作内存,线程需要读写主内存的共享变量时需要先将该变量拷贝一份副本到自己的工作内存,然后在自己的工作内存中对该变量进行所有操作,线程工作内存对变量副本完成操作之后需要将结果同步至主内存。


线程的工作内存是线程私有内存,线程间无法互相访问对方的工作内存。


为了便于理解,用图来描述一下线程对变量赋值的流程。


b067953866002216f82aec85bbc1d409_aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy8xNTY3OTEwOC00ZmI0ZTJhZmNmNmNjNjZm.png


那么问题来了,线程工作内存怎么知道什么时候又是怎样将数据同步到主内存呢?这里就轮到 JMM 出场了。JMM 规定了何时以及如何做线程工作内存与主内存之间的数据同步。


对 JMM 有了初步的了解,简单总结一下原子性、可见性、有序性。


原子性:对共享内存的操作必须是要么全部执行直到执行结束,且中间过程不能被任何外部因素打断,要么就不执行。


可见性:多线程操作共享内存时,执行结果能够及时的同步到共享内存,确保其他线程对此结果及时可见。


有序性:程序的执行顺序按照代码顺序执行,在单线程环境下,程序的执行都是有序的,但是在多线程环境下,JMM 为了性能优化,编译器和处理器会对指令进行重排,程序的执行会变成无序。


到这里,我们可以引出本文的主题了 --【线程安全】。


线程安全的本质


其实第一张图的例子是有问题的,主内存中的变量是共享的,所有线程都可以访问读写,而线程工作内存又是线程私有的,线程间不可互相访问。那在多线程场景下,图上的线程 A 和线程 B 同时来操做共享内存里的同一个变量,那么主内存内的此变量数据就会被破坏。也就是说主内存内的此变量不是线程安全的。我们来看个代码小例子帮助理解。


public class ThreadDemo {
private int x = 0;
private void count() {
x++;
}
public void runTest() {
new Thread() {
@Override
public void run() {
for (int i = 0; i < 1_000_000; i++) {
count();
}
System.out.println("final x from 1: " + x);
}
}.start();
new Thread() {
@Override
public void run() {
for (int i = 0; i < 1_000_000; i++) {
count();
}
System.out.println("final x from 2: " + x);
}
}.start();
}
public static void main(String[] args) {
new ThreadDemo().runTest();
}
}


示例代码中 runTest 方法2个线程分别执行 1_000_000 次 count() 方法, count() 方法中只执行简单的 x++ 操作,理论上每次执行 runTest 方法应该有一个线程输出的 x 结果应该是2_000_000。但实际的运行结果并非我们所想:


final x from 1: 989840


final x from 2: 1872479


我运行了10次,其中一个线程输出 x 的值为 2_000_000 只出现了2次。


final x from 1: 1000000


final x from 2: 2000000


出现这样的结果的原因也就是我们上面所说的,在多线程环境下,我们主内存的 x 变量的数据被破坏了。我们都知道完成一次 i++ 相当于执行了:


int tmp = x + 1;


x = tmp;


在多线程环境下就会出现在执行完 int tmp = x + 1; 这行代码时就发生了线程切换,当线程再次切回来的时候,x 就会被重复赋值,导致出现上面的运行结果,2个线程都无法输出 2_000_000。


下图描述了示例代码的执行时序:


2cf71d424c864d5dc26a88e2e64b6da6_aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy8xNTY3OTEwOC05NzIzOWY3ZWIwZmIwYTc5.png


那么 Java 是如何来解决上述问题来保证线程安全,保证共享内存的原子性、可见性、有序性的呢?


##/ 线程同步 /


Java 提供了一系列的关键字和类来保证线程安全。


Synchronized 关键字

Synchronized 作用


保证方法或代码块操作的原子性


Synchronized 保证⽅法内部或代码块内部资源(数据)的互斥访问。即同⼀时间、由同⼀个 Monitor(监视锁) 监视的代码,最多只能有⼀个线程在访问。


话不多说来张动图描述一下 Monitor 工作机制:

相关文章
|
2天前
|
存储 算法 Java
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
本文详解自旋锁的概念、优缺点、使用场景及Java实现。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
|
11天前
|
SQL 缓存 关系型数据库
美团面试:Mysql 有几级缓存? 每一级缓存,具体是什么?
在40岁老架构师尼恩的读者交流群中,近期有小伙伴因未能系统梳理MySQL缓存机制而在美团面试中失利。为此,尼恩对MySQL的缓存机制进行了系统化梳理,包括一级缓存(InnoDB缓存)和二级缓存(查询缓存)。同时,他还将这些知识点整理进《尼恩Java面试宝典PDF》V175版本,帮助大家提升技术水平,顺利通过面试。更多技术资料请关注公号【技术自由圈】。
美团面试:Mysql 有几级缓存? 每一级缓存,具体是什么?
|
2天前
|
SQL 安全 Java
安全问题已经成为软件开发中不可忽视的重要议题。对于使用Java语言开发的应用程序来说,安全性更是至关重要
在当今网络环境下,Java应用的安全性至关重要。本文深入探讨了Java安全编程的最佳实践,包括代码审查、输入验证、输出编码、访问控制和加密技术等,帮助开发者构建安全可靠的应用。通过掌握相关技术和工具,开发者可以有效防范安全威胁,确保应用的安全性。
12 4
|
3天前
|
存储 缓存 Java
大厂面试必看!Java基本数据类型和包装类的那些坑
本文介绍了Java中的基本数据类型和包装类,包括整数类型、浮点数类型、字符类型和布尔类型。详细讲解了每种类型的特性和应用场景,并探讨了包装类的引入原因、装箱与拆箱机制以及缓存机制。最后总结了面试中常见的相关考点,帮助读者更好地理解和应对面试中的问题。
21 4
|
12天前
|
Java
线程池内部机制:线程的保活与回收策略
【10月更文挑战第24天】 线程池是现代并发编程中管理线程资源的一种高效机制。它不仅能够复用线程,减少创建和销毁线程的开销,还能有效控制并发线程的数量,提高系统资源的利用率。本文将深入探讨线程池中线程的保活和回收机制,帮助你更好地理解和使用线程池。
40 2
|
16天前
|
Java
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是线程间通信的核心机制。
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是线程间通信的核心机制。它们通过基于锁的方式,使线程在条件不满足时进入休眠状态,并在条件成立时被唤醒,从而有效解决数据一致性和同步问题。本文通过对比其他通信机制,展示了 `wait()` 和 `notify()` 的优势,并通过生产者-消费者模型的示例代码,详细说明了其使用方法和重要性。
22 1
|
18天前
|
缓存 监控 算法
小米面试题:多级缓存一致性问题怎么解决
【10月更文挑战第23天】在现代分布式系统中,多级缓存架构因其能够显著提高系统性能和响应速度而被广泛应用。
34 3
|
30天前
|
存储 缓存 NoSQL
阿里面试题:缓存的一些常见的坑,你遇到过哪些,怎么解决的?
阿里面试题:缓存的一些常见的坑,你遇到过哪些,怎么解决的?
|
1月前
|
存储 缓存 负载均衡
Nginx代理缓存机制
【10月更文挑战第2天】
63 4
|
1月前
|
算法 Java 数据中心
探讨面试常见问题雪花算法、时钟回拨问题,java中优雅的实现方式
【10月更文挑战第2天】在大数据量系统中,分布式ID生成是一个关键问题。为了保证在分布式环境下生成的ID唯一、有序且高效,业界提出了多种解决方案,其中雪花算法(Snowflake Algorithm)是一种广泛应用的分布式ID生成算法。本文将详细介绍雪花算法的原理、实现及其处理时钟回拨问题的方法,并提供Java代码示例。
62 2

热门文章

最新文章

下一篇
无影云桌面