Volatile & transient

来源:互联网 发布:sqlserver删除唯一约束 编辑:程序博客网 时间:2024/05/29 18:50

 transient
        把一个对象的表示转化为字节流的过程称为串行化serialization,从字节流中把对象重建出来称为反串行化deserialization,transient 为不应被串行化的数据提供了一个语言级的标记数据方法。
        对象的序列化(serialization)非常影响I/O的性能,尽量少用。对不需序列化的类的域使用transient关键字,以减少序列化的数据量。

volatile
       在Java中设置变量值的操作,除了long和double类型的变量外都是原子操作,也就是说,对于变量值的简单读写操作没有必要进行同步。这在JVM 1.2之前,Java的内存模型实现总是从主存读取变量,是不需要进行特别的注意的。而随着JVM的成熟和优化,现在在多线程环境下volatile关键字的使用变得非常重要。在当前的Java内存模型下,线程可以把变量保存在本地内存(比如机器的寄存器)中,而不是直接在主存中进行读写。这就可能造成一个线程在主存中修改了一个变量的值,而另外一个线程还继续使用它在寄存器中的变量值的拷贝,造成数据的不一致。要解决这个问题,只需要像在本程序中的这样,把该变量声明为volatile(不稳定的)即可,这就指示JVM,这个变量是不稳定的,每次使用它都到主存中进行读取。一般说来,多任务环境下各任务间共享的标志都应该加volatile修饰。

在实际工作中很少有用到volatile这个关键字,今天在看一段开源代码时碰到它,查了一下它的用法 :

    用在多线程,同步变量 线程为了提高效率,将某成员变量(如A)拷贝了一份(如B),线程中对A的访问其实访问的是B。只在某些动作时才进行A和B的同步。因此存在A和B不一致的情况。volatile就是用来避免这种情况的。
     volatile告诉jvm, 它所修饰的变量不保留拷贝,直接访问主内存中的(也就是上面说的A)

Volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。

Java语言规范中指出:为了获得最佳速度,允许线程保存共享成员变量的私有拷贝,而且只当线程进入或者离开同步代码块时才与共享成员变量的原始值对比。

这样当多个线程同时与某个对象交互时,就必须要注意到要让线程及时的得到共享成员变量的变化。

volatile关键字就是提示VM:对于这个成员变量不能保存它的私有拷贝,而应直接与共享成员变量交互。

使用建议:在两个或者更多的线程访问的成员变量上使用volatile。当要访问的变量已在synchronized代码块中,或者为常量时,不必使用。

由于使用volatile屏蔽掉了VM中必要的代码优化,所以在效率上比较低,因此一定在必要时才使用此关键字。

 

-----------------------------------------------------------------------------------------------------------------

volatile是一个变量修饰符,而synchronized是一个方法或块的修饰符。所以我们使用这两种关键字来指定三种简单的存取变量的方式。

         int i1;                      int geti1() {return i1;}

volatile int i2;                      int geti2() {return i2;}

     int i3;          synchronized int geti3() {return i3;}

geti1()当前线程中立即获取在i1变量中的值。线程可以获得变量的本地拷贝,而所获得的变量的值并不一定与其他线程所获得的值相同。特别是,如果其他的线程修改了i1的值,那么当前线程获得的i1的值可能与修改后的值有所差别。实际上,Java有一种主内存的机制,使用一个主内存来保存变量当前的正确的值。线程将变量的值拷贝到自己独立的内存中,而这些线程的内存拷贝可能与主内存中的值不同。所以实际当中可能发生这样的情况,在主内存中i1的值为1,线程1和线程2都更改了i1,但是却没把更新的值传回给主内存或其他线程中,那么可能在线程1i1的值为2,线程2i1的值却为3

另一方面,geti2()可以有效的从主内存中获取i2的值。一个volatile类型的变量不允许线程从主内存中将变量的值拷贝到自己的存储空间。因此,一个声明为volatile类型的变量将在所有的线程中同步的获得数据,不论你在任何线程中更改了变量,其他的线程将立即得到同样的结果。由于线程存取或更改自己的数据拷贝有更高的效率,所以volatile类型变量在性能上有所消耗。

那么如果volatile变量已经可以使数据在线程间同步,那么synchronizes用来干什么呢?两者有两方面的不同。首先,synchronized获取和释放由监听器控制的锁,如果两个线程都使用一个监听器(即相同对象锁),那么监听器可以强制在一个时刻只有一个线程能处理代码块,这是最一般的同步。另外,synchronized还能使内存同步。在实际当中,synchronized使得所有的线程内存与主内存相同步。所以geti3()的执行过程如下:

1.   线程从监听器获取对象的锁。(这里假设监听器非锁,否则线程只有等到监听器解锁才能获取对象锁)

2.   线程内存更新所有的变量,也就是说他将读取主内存中的变量使自己的变量保证有效。(JVM会使用一个“脏”标志来最优化过程,使得仅仅具有“脏”标志变量被更新。详细的情况查询JAVA规范的17.9)

3.   代码块被执行(在这个例子中,设置返回值为刚刚从主内存重置的i3当前的值。)

4.   任何变量的变更将被写回到主内存中。但是这个例子中geti3()没有什么变化。

5.   线程释放对象的锁给监听器。

所以volatile只能在线程内存和主内存之间同步一个变量的值,而synchronized则同步在线程内存和主内存之间的所有变量的值,并且通过锁住和释放监听器来实现。显然,synchronized在性能上将比volatile更加有所消耗。

 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

volatile, 用更低的代价替代同步

为什么
使用volatile比同步代价更低?
同步的代价, 主要由其覆盖范围决定, 如果可以降低同步的覆盖范围, 则可以大幅提升程序性能. 

而volatile的覆盖范围仅仅变量级别的. 因此它的同步代价很低.

volatile原理是什么?
volatile的语义, 其实是告诉处理器, 不要将我放入工作内存, 请直接在主存操作我.(工作内存详见java内存模型)

因此, 当多核或多线程在访问该变量时, 都将直接
操作主存, 这从本质上, 做到了变量共享.

volatile的有什么优势?
1, 更大的程序吞吐量
2, 更少的代码实现多线程
3, 程序的伸缩性较好
4, 比较好理解, 无需太高的学习成本

volatile有什么劣势?
1, 容易出问题
2, 比较难设计

 

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 野兔子不吃不喝怎么办 让荷兰猪咬了怎么办 仓鼠的脚被棉花怎么办 仓鼠的脚变黑了怎么办 夏天小仓鼠生了怎么办 把仓鼠摔出血了怎么办 仓鼠摔成骨折了怎么办 孩子被仓鼠咬了怎么办 仓鼠不咬磨牙石怎么办 仓鼠妈妈跑了宝宝怎么办 买的仓鼠繁殖了怎么办 仓鼠没有鼠粮了怎么办 仓鼠被踩吐血了怎么办? 买的蓝莓太酸怎么办 荷兰猪夏天掉毛怎么办 被猫抓伤肿了怎么办 荷兰猪鼻子破了怎么办 荷兰猪吃了包菜怎么办 龙猫不爱吃主粮怎么办 荷兰猪躲起来了怎么办 荷兰猪一直叫是怎么办 龙猫不吃粗的主粮怎么办 转龙猫中暑症状+龙猫中暑怎么办 火车上空调太冷怎么办 格力空调太冷怎么办 未满月龙猫宝宝怎么办 房间小空调太冷怎么办 奶猫半夜不睡觉怎么办 龙猫不喜欢吃草粒怎么办 龙猫一直在发抖怎么办 龙猫牙齿掉了怎么办 龙猫晚上很吵怎么办 小狗把手咬破了怎么办 夏天仓鼠太热了怎么办 仓鼠玩转轮会摔怎么办 如何发截图不会发现吐怎么办 嘴巴烂了药过敏怎么办 非法校车被扣了怎么办 手指削掉一块肉怎么办 在广州误走brt道怎么办 平安eq测试没过怎么办