一 概述
1.volatile
保证共享数据一旦被修改就会立即同步到共享内存(堆或者方法区)中。
2.线程访问堆中数据的过程
线程在栈中建立一个变量的副本,修改完毕后将数据同步到堆中。
3.指令重排
为了提高执行效率,CPU会将没有依赖关系的指令重新排序。如果希望控制重新排序,可以使用volatile修饰一个变量,包含该变量的指令前后的指令各自独立排序,前后指令不能交叉排序。
二 常见问题及应对
1.原子性问题
所谓原子性,指的是一个操作不可中断,即在多线程并发的环境下,一个操作一旦开始,就会在同一个CPU时间片内执行完毕。如果同一个线程的多个操作在不同的CPU时间片上执行,由于中间出现停滞,后面的操作在执行时可能某个共享数据被其他线程修改,而该修改并未同步到当前线程中,导致当前线程操作的数据与实际不符,这种由于执行不连贯导致的数据不一致问题被称作原子性问题。
2.可见性问题
可见性问题的出现与线程访问共享数据的方式有关。线程访问堆(方法区)中的变量时,先在栈中建立一个变量的副本,修改后再同步到堆中。如果一个线程刚建立副本,这时另一线程修改了变量,尚未同步到堆中,这时就会出现两个线程操作同一变量的同一种状态的现象,比如i=9,变量i的初始值为9,每一个线程的操作都是减1。两个线程A与B同时访问变量,B先执行i-1,在将结果i=8同步到堆中前,A线程也执行i-1,这时i=9的状态就被执行两次,出现线程安全问题。
线程安全问题产生的原因:一个线程对共享数据的修改不能立即为其他线程所见。
volatile提供了一种解决方案:
一旦一个线程修改了被volatile修饰的共享数据,这种修改就会立即同步到堆中,这样其他数据从堆中访问共享数据时始终获得的是在多个线程中的最新值。
volatile的缺陷:
volatile只能保证一个线程从堆中获取数据时获取的是当前所有线程中的最新值,假如一个线程已经从堆中复制了数据,在操作完成前,其他线程修改了数据,修改后的数据并不会同步到当前线程中。
3.有序性问题
为了提高执行效率,CPU会对那些没有依赖关系的指令重新排序,重新排序后的执行结果与顺序执行结果相同。
例如,在源代码中:
int i=0;
int y=1;
CPU在执行时可能先执行“int y=1;”,接着执行“int i=0;”,执行结果与顺序执行结果相同。
指令重排在单线程环境下是安全的,在多线程环境下就可能出现问题。比如:
线程A:
s=new String("sssss");//指令1flag=false;//指令2
线程B:
while(flag){ doSome(); } s.toUpperCase();//指令3
如果线程A顺序执行,即执行指令1,再执行指令2,线程B的执行不会出现问题。指令重排后,假如线程A先执行指令2,
这是flag=true,切换到线程2,终止循环,执行指令3,由于s对象尚未创建就会出现空指针异常。
有序性问题产生的原因:
一个线程对其他线程对共享数据的修改操作有顺序要求,比如线程B要求线程A先执行指令1,再执行指令2,由于指令重排,实际并未按照要求的顺序执行,这时就出现了线程安全问题。
解决思路:
利用同步机制,使得同一时间只有一个线程可以访问共享数据,效率低。
使用volatile,一个指令包含volatile修饰的变量,那么这条指令的执行顺序不变,该指令前后的指令可以各自独立重排,无法交叉重排。
参考:
以上是多线程并发的一些问题分享的详细内容。更多信息请关注PHP中文网其他相关文章!

Java主要用于构建桌面应用、移动应用、企业级解决方案和大数据处理。1.企业级应用:通过JavaEE支持复杂应用,如银行系统。2.Web开发:使用Spring、Hibernate简化开发,SpringBoot快速搭建微服务。3.移动应用:仍是Android开发主要语言之一。4.大数据处理:Hadoop和Spark基于Java处理海量数据。5.游戏开发:适用于中小型游戏开发,如Minecraft。

如何将Java开发工具设置为中文界面?可以通过以下步骤实现:Eclipse:Window->Preferences->General->Appearance->I18nsupport->Language->Chinese(Simplified),然后重启Eclipse。IntelliJIDEA:Help->FindAction->输入"switchlanguage"->选择"SwitchIDELanguage&q

学习Java并达到工作水平通常需要6到12个月,对于有编程基础的人可能缩短至3到6个月。1)零基础学习者需6-12个月掌握基础和常用库。2)有编程基础者可能3-6个月内掌握。3)就业时间在学习9-18个月后,实际项目和实习可加速进程。

在Java中,new操作符用于创建对象,其过程包括:1)在堆内存中分配空间,2)初始化对象,3)调用构造函数,4)返回对象引用。理解这些步骤有助于优化内存使用和提升应用程序性能。

在Java中定义数组的语法是:1.数据类型[]数组名=new数据类型[数组长度];2.数据类型数组名[]=new数据类型[数组长度];3.数据类型[]数组名={元素列表};数组是对象,可为null,下标从0开始,使用时需注意潜在的错误如NullPointerException和ArrayIndexOutOfBoundsException。

new关键字在Java中用于创建对象实例。1)它告诉JVM分配内存并调用构造函数初始化对象。2)使用new可以强制创建新对象,即使内容相同。3)构造函数允许自定义初始化。4)频繁使用new可能导致性能问题和内存泄漏。5)需要使用try-catch处理可能的异常。6)匿名内部类是new的高级用法。

解决Java中的中文乱码问题可以通过以下步骤:1.设置正确的字符编码,如UTF-8或GBK,确保文件、数据库和网络通信使用相同编码。2.使用Java的字符编码转换类进行必要的编码转换。3.通过调试工具和日志验证编码是否正确,确保在不同环境下中文显示正常。

Java中的异常分为检查型异常和非检查型异常。检查型异常必须显式处理,否则编译器报错,常用于可恢复错误,如文件未找到;非检查型异常无需显式处理,常用于编程错误,如空指针异常。


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

禅工作室 13.0.1
功能强大的PHP集成开发环境

SublimeText3汉化版
中文版,非常好用

Dreamweaver CS6
视觉化网页开发工具

VSCode Windows 64位 下载
微软推出的免费、功能强大的一款IDE编辑器

ZendStudio 13.5.1 Mac
功能强大的PHP集成开发环境