fields怎么读(dashing怎么读)
- 前端设计
- 2023-08-13
- 112
大家好,fields怎么读相信很多的网友都不是很明白,包括dashing怎么读也是一样,不过没有关系,接下来就来为大家分享关于fields怎么读和dashing怎么读的...
大家好,fields怎么读相信很多的网友都不是很明白,包括dashing怎么读也是一样,不过没有关系,接下来就来为大家分享关于fields怎么读和dashing怎么读的一些知识点,大家可以关注收藏,免得下次来找不到哦,下面我们开始吧!
volatile关键字是什么
主要从以下三点讲解volatile关键字:
volatile关键字是什么?volatile关键字能解决什么问题?使用场景是什么?volatile关键字实现的原理?volatile关键字是什么?在Sun的JDK官方文档是这样形容volatile的:
TheJavaprogramminglanguageprovidesasecondmechanism,volatilefields,thatismoreconvenientthanlockingforsomepurposes.Afieldmaybedeclaredvolatile,inwhichcasetheJavaMemoryModelensuresthatallthreadsseeaconsistentvalueforthevariable.也就是说,如果一个变量加了volatile关键字,就会告诉编译器和JVM的内存模型:这个变量是对所有线程共享的、可见的,每次JVM都会读取最新写入的值并使其最新值在所有CPU可见。volatile可以保证线程的可见性并且提供了一定的有序性,但是无法保证原子性。在JVM底层volatile是采用内存屏障来实现的。
通过这段话,我们可以知道volatile有两个特性:
保证可见性、不保证原子性
禁止指令重排序原子性和可见性原子性是指一个操作或多个操作要么全部执行并且执行的过程不会被任何因素打断,要么都不执行。性质和数据库中事务一样,一组操作要么都成功,要么都失败。看下面几个简单例子来理解原子性:
i==0;//1
j=i;//2
i++;//3
i=j+1;//4
在看答案之前,可以先思考一下上面四个操作,哪些是原子操作?哪些是非原子操作?
答案揭晓:
1——是:在Java中,对基本数据类型的变量赋值操作都是原子性操作(Java有八大基本数据类型,分别是byte,short,int,long,char,float,double,boolean)
2——不是:包含两个动作:读取i值,将i值赋值给j
3——不是:包含了三个动作:读取i值,i+1,将i+1结果赋值给i
4——不是:包含了三个动作:读取j值,j+1,将j+1结果赋值给i
也就是说,只有简单的读取、赋值(而且必须是将数字赋值给某个变量,变量之间的相互赋值不是原子操作)才是原子操作。
注:由于以前的操作系统是32位,64位数据(long型,double型)在Java中是8个字节表示,一共占用64位,因此需要分成两次操作采用完成一个变量的赋值或者读取操作。随着64位操作系统越来越普及,在64位的HotSpotJVM实现中,对64位数据(long型,double型)做原子性处理(由于JVM规范没有明确规定,不排除别的JVM实现还是按照32位的方式处理)。
在单线程环境中我们可以认为上述步骤都是原子性操作,但是在多线程环境下,Java只保证了上述基本数据类型的赋值操作是原子性的,其他操作都有可能在运算过程中出现错误。为此在多线程环境下为了保证一些操作的原子性引入了锁和synchronized等关键字。
上面说到volatile关键字保证了变量的可见性,不保证原子性。原子性已经说了,下面说下可见性。
可见性其实和Java内存模型的设定有关:Java内存模型规定所有的变量都是存在主存(线程共享区域)当中,每个线程都有自己的工作内存(私有内存)。线程对变量的所有操作都必须在工作内存中进行,而不直接对主存进行操作。并且每个线程不能访问其他线程的工作内存。
举个简单栗子:
比如上面i++操作,在Java中,执行i++语句:
执行线程首先从主存中读取i(原始值)到工作内存中,然后在工作内存中执行运算+1操作(主存的i值未变),最后将运算结果刷新到主存中。
数据运算是在执行线程的私有内存中进行的,线程执行完运算后,并不一定会立即将运算结果刷新到主存中(虽然最后一定会更新主存),刷新到主存动作是由CPU自行选择一个合适的时间触发的。假设数值未更新到主存之前,当其他线程去读取时(而且优先读取的是工作内存中的数据而非主存),此时主存中可能还是原来的旧值,就有可能导致运算结果出错。
以下代码是测试代码:
packagecom.wupx.test;/**
*@authorwupx
*@date2019/10/31
*/
publicclassVolatileTest{
privatebooleanflag=false;
classThreadOneimplementsRunnable{
@Override
publicvoidrun(){
while(!flag){
System.out.println("执行操作");
try{
Thread.sleep(1000L);
}catch(InterruptedExceptione){
e.printStackTrace();
}
}
System.out.println("任务停止");
}
}
classThreadTwoimplementsRunnable{
@Override
publicvoidrun(){
try{
Thread.sleep(2000L);
System.out.println("flag状态改变");
flag=true;
}catch(InterruptedExceptione){
e.printStackTrace();
}
}
}
publicstaticvoidmain(String[]args){
VolatileTesttestVolatile=newVolatileTest();
Threadthread1=newThread(testVolatile.newThreadOne());
Threadthread2=newThread(testVolatile.newThreadTwo());
thread1.start();
thread2.start();
}
}
上述结果有可能在线程2执行完flag=true之后,并不能保证线程1中的while能立即停止循环,原因在于flag状态首先是在线程2的私有内存中改变的,刷新到主存的时机不固定,而且线程1读取flag的值也是在自己的私有内存中,而线程1的私有内存中flag仍未false,这样就有可能导致线程仍然会继续while循环。运行结果如下:
执行操作执行操作
执行操作
flag状态改变
任务停止
避免上述不可预知问题的发生就是用volatile关键字修饰flag,volatile修饰的共享变量可以保证修改的值会在操作后立即更新到主存里面,当有其他线程需要操作该变量时,不是从私有内存中读取,而是强制从主存中读取新值。即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
指令重排序一般来说,处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。
比如下面的代码
inti=0;
booleanflag=false;
i=1;//1
flag=true;//2
代码定义了一个int型变量,定义了一个boolean类型变量,然后分别对两个变量进行赋值操作。从代码顺序上看,语句1是在语句2前面的,那么JVM在真正执行这段代码的时候会保证语句1一定会在语句2前面执行吗?不一定,为什么呢?这里可能会发生指令重排序(InstructionReorder)。
语句1和语句2谁先执行对最终的程序结果并没有影响,那么就有可能在执行过程中,语句2先执行而语句1后执行。
但是要注意,虽然处理器会对指令进行重排序,但是它会保证程序最终结果会和代码顺序执行结果相同,那么它靠什么保证的呢?再看下面一个例子:
inta=10;//1intr=2;//2
a=a+3;//3
r=a*a;//4
这段代码执行的顺序可能是1->2->3->4或者是2->1->3->4,但是3和4的执行顺序是不会变的,因为处理器在进行重排序时是会考虑指令之间的数据依赖性,如果一个指令Instruction2必须用到Instruction1的结果,那么处理器会保证Instruction1会在Instruction2之前执行。
虽然重排序不会影响单个线程内程序执行的结果,但是多线程呢?下面看一个例子:
//线程1Stringconfig=initConfig();//1
booleaninited=true;//2
//线程2
while(!inited){
sleep();
}
doSomeThingWithConfig(config);
上面代码中,由于语句1和语句2没有数据依赖性,因此可能会被重排序。假如发生了重排序,在线程1执行过程中先执行语句2,而此时线程2会以为初始化工作已经完成,那么就会跳出while循环,去执行doSomeThingWithConfig(config)方法,而此时config并没有被初始化,就会导致程序出错。
从上面可以看出,指令重排序不会影响单个线程的执行,但是会影响到线程并发执行的正确性。
那么volatile关键字修饰的变量禁止重排序的含义是:
当程序执行到volatile变量的读操作或者写操作时,在其前面的操作肯定已经全部进行,且对后面的操作可见,在其后面的操作肯定还没有进行
在进行指令优化时,不能将volatile变量之前的语句放在对volatile变量的读写操作之后,也不能把volatile变量后面的语句放到其前面执行举个栗子:
x=0;//1
y=1;//2
volatilez=2;//3
x=4;//4
y=5;//5
变量z为volatile变量,那么进行指令重排序时,不会将语句3放到语句1、语句2之前,也不会将语句3放到语句4、语句5后面。但是语句1和语句2、语句4和语句5之间的顺序是不作任何保证的,并且volatile关键字能保证,执行到语句3时,语句1和语句2必定是执行完毕了的,且语句1和语句2的执行结果是对语句3、语句4、语句5是可见的。
回到之前的例子:
//线程1
Stringconfig=initConfig();//1
volatilebooleaninited=true;//2
//线程2
while(!inited){
sleep();
}
doSomeThingWithConfig(config);
之前说这个例子提到有可能语句2会在语句1之前执行,那么就可能导致执行doSomThingWithConfig()方法时就会导致出错。
这里如果用volatile关键字对inited变量进行修饰,则可以保证在执行语句2时,必定能保证config已经初始化完毕。
volatile应用场景synchronized关键字是防止多个线程同时执行一段代码,那么就会很影响程序执行效率,而volatile关键字在某些情况下性能要优于synchronized,但是要注意volatile关键字是无法替代synchronized关键字的,因为volatile关键字无法保证操作的原子性。通常来说,使用volatile必须具备以下三个条件:
对变量的写入操作不依赖变量的当前值,或者能确保只有单个线程更新变量的值
该变量不会与其他状态变量一起纳入不变性条件中在访问变量时不需要加锁上面的三个条件只需要保证是原子性操作,才能保证使用volatile关键字的程序在高并发时能够正确执行。建议不要将volatile用在getAndOperate场合,仅仅set或者get的场景是适合volatile的。
常用的两个场景是:
状态标记量
volatilebooleanflag=false;
while(!flag){
doSomething();
}
publicvoidsetFlag(){
flag=true;
}
volatilebooleaninited=false;
//线程1
context=loadContext();
inited=true;
//线程2
while(!inited){
sleep();
}
doSomethingwithconfig(context);
DCL双重校验锁-单例模式publicclassSingleton{
privatevolatilestaticSingletoninstance=null;
privateSingleton(){
}
/**
*当第一次调用getInstance()方法时,instance为空,同步操作,保证多线程实例唯一
*当第一次后调用getInstance()方法时,instance不为空,不进入同步代码块,减少了不必要的同步
*/
publicstaticSingletongetInstance(){
if(instance==null){
synchronized(Singleton.class){
if(instance==null){
instance=newSingleton();
}
}
}
returninstance;
}
}
使用volatile的原因在上面解释重排序时已经讲过了。主要在于instance=newSingleton(),这并非是一个原子操作,在JVM中这句话做了三件事情:
给instance分配内存
调用Singleton的构造函数来初始化成员变量将instance对象指向分配的内存库存空间(执行完这步instance就为非null了)但是JVM即时编译器中存在指令重排序的优化,也就是说上面的第二步和第三步顺序是不能保证的,最终的执行顺序可能是1-2-3,也可能是1-3-2。如果是后者,线程1在执行完3之后,2之前,被线程2抢占,这时instance已经是非null(但是并没有进行初始化),所以线程2返回instance使用就会报空指针异常。
volatile特性是如何实现的呢?前面讲述了关于volatile关键字的一些使用,下面我们来探讨一下volatile到底如何保证可见性和禁止指令重排序的。
在《深入理解Java虚拟机》这本书中说道:
观察加入volatile关键字和没有加入volatile关键字时所生成的汇编代码发现,加入volatile关键字时,会多出一个lock前缀指令。接下来举个栗子:
volatile的Integer自增(i++),其实要分成3步:
读取volatile变量值到local
增加变量的值把local的值写回,让其它的线程可见这3步的JVM指令为:
mov0xc(%r10),%r8d;Load
inc%r8d;Increment
mov%r8d,0xc(%r10);Store
lockaddl$0x0,(%rsp);StoreLoadBarrier
lock前缀指令实际上相当于一个内存屏障(也叫内存栅栏),内存屏障会提供3个功能:
它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成(满足禁止重排序)
它会强制将对缓存的修改操作立即写入主存(满足可见性)如果是写操作,它会导致其他CPU中对应的缓存行无效(满足可见性)volatile变量规则是happens-before(先行发生原则)中的一种:对一个变量的写操作先行发生于后面对这个变量的读操作。(该特性可以很好解释DCL双重检查锁单例模式为什么使用volatile关键字来修饰能保证并发安全性)
总结变量声明为volatile类型时,编译器与运行时都会注意到这个变量是共享的,不会将该变量上的操作与其他内存操作一起重排序。volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取volatile类型的变量时总会返回最新写入的值。
在访问volatile变量时不会执行加锁操作,也就不会使执行线程阻塞,因此volatile变量是比sychronized关键字更轻量级的同步机制。
加锁机制既可以确保可见性和原子性,而volatile变量只能确保可见性。
想了解更多Java相关,百度搜索圈T社区www.aiquanti.com,免费视频教程。纯干货
读原版哈利波特的好处
读原版哈利波特这本书有很多好处。首先,它可以帮助读者培养阅读兴趣,提高阅读水平,提高语言能力。其次,通过对魔法世界的描述,可以让读者想象力得到锻炼,增强创造力。此外,哈利波特也传递了积极的价值观,例如友情、正义、勇气等,有助于塑造读者的人生观。最后,哈利波特系列的全球影响力也促进了跨文化沟通,有利于推动文化交流和理解。
nine和pig和rice发音相同吗
如果是在问三个单词中字母i的发音是否相同,nine和rice是相同的。
nine——字母i的发音为字母本身音,音标[ai]
pig——字母i的发音为短元音,音标[i]
rice——字母i的发音为字母本身音,音标[ai]
房车自驾游怎么样
在欧美、澳洲会很爽,但在国内房车营地等相关配套设施还跟不上,洗澡、用电、停车、安全等多方面都存在问题。我去年十一月份租了一台房车从重庆自驾去海南环岛游,因为房车公司搞活动,租金只要1块钱每天,这就再次体现出我自由职业者的好处了,头天晚上看到朋友圈有人发活动,当场预定,第二天带上家人,带上电脑就出发了。
租金是便宜,但押金交了五万,油费和过路费一共3000,平均每公里成本1块吧,2.7的海狮,百公里差不多16个油,车子还到三亚坐飞机回的重庆,机票200,再次体现自由职业者淡季出行的福利。
全程住的酒店,一天都没有住车上,洗澡不方便。但这种方式还是很适合带孩子和老人一起旅行,他们不累,随时可以睡觉。10天行程,一家五口,吃住行玩一共用了15000,性价比还是非常高了,孩子特别喜欢,毕竟房车感觉很新奇。
淡季的三亚还是很不错的,海鲜也不贵,特别推荐海棠湾万豪万丽酒店,豪华海景标准间780一晚,性价比超高,私家海滩,清爽感动世界!
近三年的梦想是买一台能越野的小房车,在孩子寒暑假时一家三口去环球旅行,我的朋友圈里就有这么一家子,已经跑了三年十多万公里了,靠给媒体供稿和商业赞助为生,孩子基本上是在车上长大的,很有意义的童年。
一个佘一个田读什么
一个佘一个田读畲。畲是一个多音字,汉语拼音为shē和yú,字形结构为上下结构,共计12画。一般读作shē,意思是火耕地,指粗放耕种的田地。畲也是我国的一个少数民族,畲族,主要居住在浙江省和福建省。当畲读住yú时,是指开垦了二、三年的熟田。
一个土一个中念什么
埝拼音:niàn
田里或浅水里用于挡水的土埂〖alowbankbetweenfields〗。如:打埝
淮北盐场交货、换船的地方叫埝〖dock〗。也称为堰。如:太平埝;临浦埝埝
沂蒙地区方言,地方的意思。
关于fields怎么读到此分享完毕,希望能帮助到您。
本文链接:http://www.xinin56.com/qianduan/4517.html