`

native,strictfp,transient,volatile区别于联系

阅读更多
native,strictfp,transient,volatile区别于联系
2010-07-12 17:50
Java关键字(keywords)
abstract    default    if            private      this
boolean     do         implements    protected    throw
break       double     import        public       throws
byte        else       instanceof    return       transient
case        extends    int           short        try
catch       final      interface     static       void
char        finally    long          strictfp     volatile
class       float      native        super        while
const       for        new           switch
continue    goto       package       synchronized


以上是java specifications中定义的keywords,一共48个,其中常见的三个看似是关键字的true, false, null,都不是关键字,而是作为一个单独标识类型。
其中,不常用到的关键字有:const,goto,native,strictfp,transient,volatile。
const和goto为java中的保留字。
1. native
native是方法修饰符。Native方法是由另外一种语言(如c/c++,FORTRAN,汇编)实现的本地方法。因为在外部实现了方法,所以在java代码中,就不需要声明了,有点类似于借口方法。Native可以和其他一些修饰符连用,但是abstract方法和Interface方法不能用native来修饰。
Example:
Java代码
public interface TestInterface {  
     void doMethod();  
}  
public class Test implements TestInterface {  
    public native void doMethod();  
    private native int doMethodB();  
  public native synchronized String doMethodC();  
  static native void doMethodD();  


为什么需要使用native method?请参考:
http://www.javaeye.com/topic/72543 java Native Method初涉
2. strictfp
修饰类和方法,意思是FP-strict,精确浮点,符合IEEE-754规范的。当一个class或interface用strictfp声明,内部所有的float和double表达式都会成为strictfp的。Interface method不能被声明为strictfp的,class的可以。
Example:
Java代码
strictfp interface FPTest {  
     void methodA();  
}  
class FPClass implements FPTest {  
    public void methodA() {  
     }  
    public void methodB() {  
  }  
  public strictfp void methodC() {  
  }  
}  
class FPClassB {  
    strictfp void methodA() {  
     }  


3.transient
变量修饰符。标记为transient的变量,在对象存储时,这些变量状态不会被持久化。当对象序列化的保存在存储器上时,不希望有些字段数据被保存,为了保证安全性,可以把这些字段声明为transient。
4. volatile
volatile修饰变量。在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
看看Java Language Specification中的例子。
条件:一个线程不停的调用方法one(),一个线程不停的调用方法two()。我测试过多次,这种情况好像一直没有出现。
Java代码
class Test {  
    static int i = 0, j = 0;  
    static void one() { i++; j++; }  
    static void two() {  
         System.out.println("i=" + i + " j=" + j);  
     }  


结果偶尔会出现j大于i的情况,因为方法没有同步,所以会出现i和j可能不是一次更新。一种防止这种情况发生的办法就是声明两个方法为synchronized 的。
Java代码
class Test {  
    static int i = 0, j = 0;  
    static synchronized void one() { i++; j++; }  
    static synchronized void two() {  
         System.out.println("i=" + i + " j=" + j);  
     }  


这样可以防止两个方法同时被执行,还可以保证j和i被同时更新,这样一来i和j的值一直是一样的。
另外一种途径就是把i和j声明为volatile。
Java代码
class Test {  
    static volatile int i = 0, j = 0;  
    static void one() { i++; j++; }  
    static void two() {  
         System.out.println("i=" + i + " j=" + j);  
     }  
}



Java Volatile transient

Java 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)


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



Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1591874

http://blog.csdn.net/caoi/archive/2006/03/28/640939.aspx

谈谈Java关键字transient和volatile。

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

         参考:Serializable(中文,英文)
                     Java Serialization Example
                     Serializable java序列化可能带来的问题
                     空接口的使用(给JAVA设计开发新手的一些建议和意见)

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

http://blog.csdn.net/ai92/archive/2005/03/08/315183.aspx

初探关键字volatile    

第一次接触到关键字volatile,不知为何物,只是模糊的记得java关键字里面好像有它。查了些资料,整理如下:

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

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

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

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

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

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

http://blog.csdn.net/majorboy/archive/2005/09/09/475811.aspx

关于volatile和synchronized    

这个可能是最好的对比volatile和synchronized作用的文章了。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,但是却没把更新的值传回给主内存或其他线程中,那么可能在线程1中i1的值为2,线程2中i1的值却为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更加有所消耗。

破除java神话之三:原子操作都是线程安全的    

java中原子操作是线程安全的论调经常被提到。根据定义,原子操作是不会被打断地的操作,因此被认为是线程安全的。实际上有一些原子操作不一定是线程安全的。

这个问题出现的原因是尽量减少在代码中同步关键字。同步会损害性能,虽然这个损失因JVM不同而不同。另外,在现代的JVM中,同步的性能正在逐步提高。尽管如此,使用同步仍然是有性能代价的,并且程序员永远会尽力提高他们的代码的效率,因此这个问题就延续了下来。

在java 中,32位或者更少位数的赋值是原子的。在一个32位的硬件平台上,除了double和long型的其它原始类型通常都是使用32位进行表示,而 double和long通常使用64位表示。另外,对象引用使用本机指针实现,通常也是32位的。对这些32位的类型的操作是原子的。

这些原始类型通常使用32位或者64位表示,这又引入了另一个小小的神话:原始类型的大小是由语言保证的。这是不对的。java语言保证的是原始类型的表数范围而非JVM中的存储大小。因此,int型总是有相同的表数范围。在一个JVM上可能使用32位实现,而在另一个JVM上可能是64位的。在此再次强调:在所有平台上被保证的是表数范围,32位以及更小的值的操作是原子的。

那么,原子操作在什么情况下不是线程安全的?主要的一点是他们也许确实是线程安全的,但是这没有被保证!java线程允许线程在自己的内存区保存变量的副本。允许线程使用本地的私有拷贝进行工作而非每次都使用主存的值是为了提高性能。考虑下面的类:


class RealTimeClock
{
private int clkID;
public int clockID()
{
return clkID;
}
public void setClockID(int id)
{
clkID = id;
}
//...
}

现在考虑RealTimeClock的一个实例以及两个线程同时调用setClockID和clockID,并发生以下的事件序列:

T1 调用setClockID(5)
T1将5放入自己的私有工作内存
T2调用setClockID(10)
T2将10放入自己的私有工作内存
T1调用clockID,它返回5
5是从T1的私有工作内存返回的

对clockI的调用应该返回10,因为这是被T2设置的,然而返回的是5,因为读写操作是对私有工作内存的而非主存。赋值操作当然是原子的,但是因为JVM允许这种行为,因此线程安全不是一定的,同时,JVM的这种行为也不是被保证的。

两个线程拥有自己的私有拷贝而不和主存一致。如果这种行为出现,那么私有本机变量和主存一致必须在以下两个条件下:

1、变量使用volatile声明
2、被访问的变量处于同步方法或者同步块中

如果变量被声明为volatile,在每次访问时都会和主存一致。这个一致性是由java语言保证的,并且是原子的,即使是64位的值。(注意很多JVM没有正确的实现volatile关键字。你可以在www.javasoft.com找到更多的信息。)另外,如果变量在同步方法或者同步块中被访问,当在方法或者块的入口处获得锁以及方法或者块退出时释放锁是变量被同步。
使用任何一种方法都可以保证ClockID返回10,也就是正确的值。变量访问的频度不同则你的选择的性能不同。如果你更新很多变量,那么使用volatile可能比使用同步更慢。记住,如果变量被声明为volatile,那么在每次访问时都会和主存一致。与此对照,使用同步时,变量只在获得锁和释放锁的时候和主存一致。但是同步使得代码有较少的并发性。

如果你更新很多变量并且不想有每次访问都和主存进行同步的损失或者你因为其它的原因想排除并发性时可以考虑使用同步。





分享到:
评论

相关推荐

    JAVA笔试总结 -- 非常全面

    native,transient,volatile,strictfp,CMM,synchronized,java socket,压缩与解压缩,多线程,垃圾回收算法,JVM ClassLoader,IO流,反射机制,JNDI, GUI布局管理器,JMS, Java Mail, JNDI reference,java事件处理...

    99乘法表java源码-biji:课堂笔记

    99乘法表java源码 ls 查看目录下文件 java规则 代码都定义在类中,用class...char fianlly native strictfp void class float new super volatile continue for null switch while default if package enum synchroniz

    java面试100题

    Java 中的关键字有 native、strictfp、transient 和 volatile 等。 1. native 修饰符,表示方法是由另外一种语言(如 c/c++,FORTRAN,汇编)实现的本地方法。 2. strictfp 修饰符,表示类或方法遵守 IEEE-754 规范...

    java修饰词的总结.doc

    Java 语言中有多种修饰词,总共定义了 11 种,包括 public、protected、private、abstract、static、final、native、strictfp、synchronized、volatile 和 transient。这些修饰词可以应用于类、接口、成员、方法、...

    JAVA学习笔记.pdf

    其中,修饰符可以是static、abstract、final、native、strictfp、synchronized等,结果类型是方法的返回类型,方法名是方法的名称,参数列表是方法的参数列表,throws子句是方法可能抛出的异常。 成员方法的修饰符...

    Java编程中常用修饰词使用方法

    介绍:被定义成 final 的类不允许出现子类,不能被覆盖(不应用于动态查询),字段值不允许被修改。final 修饰词可以用来确保类、方法、字段和变量的不可变性。 5. abstract 修饰词 修饰对象:类、接口、方法 介绍...

    java修饰词

    - 静态字段属于类,不依赖于任何实例,所有实例共享同一个静态字段。 - 静态方法与类相关联,而不是实例,无法访问非静态成员。 - 静态初始化块在类加载时执行,而非实例化时。 4. **final**: - `final`修饰的...

    Java基础标识符关键字数据类型PPT教案学习.pptx

    private、public、protected、final、static、abstract、synchronized、volatile、strictfp、native、transient 等修饰符关键字;try、catch、finally、throw、throws 等异常处理关键字;new、extends、implements、...

    Java入门——Java修饰词总结

    它确保了线程间的可见性和有序性,即任何线程对`volatile`字段的修改对其他线程立即可见,且不会发生指令重排序。 11. **transient**: - 使用对象:字段 - 介绍:`transient`修饰符用于指定在序列化过程中不应被...

    java关键字.pdf

    2. 类、方法和变量修饰符:abstract、final、static、transient、volatile - abstract表示抽象类或方法,抽象类不能实例化,抽象方法必须在子类中实现。 - final表示最终的,用于类表示不可继承,用于方法表示不可...

    java修饰符介绍

    - **native**:声明方法是原生方法,其实现由C/C++或其他本地库提供,通过JNI(Java Native Interface)调用。 - **synchronized**:同步方法,确保同一时间只有一个线程可以执行该方法,用于线程安全。 - **...

    修饰符

    6. native, strictfp和transient: - `native`: 用于表示方法是用其他语言(如C++)实现的,Java本身不提供实现。 - `strictfp`: 用于确保浮点运算的精确性,遵循IEEE 754标准,避免平台差异带来的计算结果不一致。...

    SpringSide 团队的编码规范

    - 修饰符顺序:public, protected, private, abstract, static, final, transient, volatile, synchronized, native, strictfp。 - 类和接口的声明顺序应遵循一定的结构,包括静态字段、静态初始化块、字段、初始...

    基础标识符关键字数据类型PPT课件.pptx

    - **方法、变量和类修饰符**:private、public、protected、final、static、abstract、synchronized、volatile、strictfp、native和transient,这些修饰符用于控制访问权限、确定变量和方法的特性,以及类的实现方式...

    JAVA学习笔记[参考].pdf

    方法修饰符如`static`、`abstract`、`final`、`native`、`strictfp`和`synchronized`分别表示静态方法、抽象方法、不可覆盖的方法、本地方法、浮点计算精确度和同步方法。 - 非`private`的方法可以被子类继承,方法...

    深入浅出谈java修饰符共6页.pdf.zip

    - `strictfp`: 严格浮点,确保浮点计算在所有平台上具有可预测的结果,避免由于平台差异导致的浮点运算精度问题。 - `interface`: 接口,定义了一组抽象方法,用于实现多继承。Java 8引入了默认方法(用`default`...

    Javaclass文件格式之访问标志信息-动力节点共6页

    7. **变量访问标志**:如`static`、`volatile`和`transient`,它们分别表示字段是否属于类而不是实例,是否保证内存可见性和是否不应序列化。 8. **方法访问标志**:例如`native`表示方法的实现是在本地代码中,`...

    Java的关键字与标识符小结

    9. 其他关键字:包括native、strictfp、transient、volatile、assert、goto、const和enum等8个关键字。 标识符是用户在编程时给类、变量、常量、方法(函数)、语句块等起的名字。Java语言中标识符不包括关键字,而...

    JAVA程序设计课件-变量.pptx

    * transient * native * strictfp * ... 四、运算符 Java中的运算符是用于执行数学运算、逻辑运算、赋值运算等操作的符号。Java中的运算符有: * 算术运算符:+、-、\*、/、%、++、-- * 赋值运算符:=、+=、-=、\...

    java常用的修饰符

    - **native**: 表示方法的实现不在Java代码中,而是由操作系统提供,如JNI(Java Native Interface)。 - **static**: 除了用于字段,还可以修饰方法,创建静态方法,不依赖于类的实例即可调用。 这些修饰符组合...

Global site tag (gtag.js) - Google Analytics