关于native,transient,volatile,synchronized四个关键字的使用

本文解析了Java中的四个关键字:native、transient、volatile和synchronized的使用场景与原理,特别是volatile和synchronized的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

[转载]关于native,transient,volatile,synchronized四个关键字的使用

native 
当你需要调用本地程序的时候 
transient 
你的类实现了java.io.Serializable而你又不想保存某个字段的时候 
volatile 
这个字段会被其他线程(直接或者间接)访问到,而你想保证每个线程都能得到最新的数据 
(性能上肯定有损耗的,为了安全牺牲性能的事情多着去了)
------------------------------------------------------------------------------------ 
3,volatile 
原优快云有一篇介绍过,现贴回来。 
volatile修饰变量。在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。  
看看Java Language Specification中的例子。  
条件:一个线程不停的调用方法one(),一个线程不停的调用方法two()。我测试过多次,这种情况好像一直没有出现。  
代码 
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 的。  
代码 
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。  
代码 
class Test {    
    static volatile int i = 0, j = 0;    
    static void one() { i++; j++; }    
    static void two() {    
        System.out.println("i=" + i + " j=" + j);    
    }    
}   
------------------------------------------------------------------------------------ 
2,transient 
transient是一个变量修饰符,标记为transient的变量,在对一个对象进行序列化时,这些变量状态不会被序列化。 
例如,假设某个类的成员变量是transient,那么当通过ObjectOutputStream把这个类的某个实例保存到磁盘上时,实际上transient变量的值是不会保存的。 
当对象序列化的保存在存储器上时,不希望有些字段数据被保存,为了保证安全性,可以把这些字段声明为transient。 
要更明白,可以看一些序列化的内容。
------------------------------------------------------------------------------------ 
1,native是方法修饰符。Native方法是由另外一种语言(如c/c++,FORTRAN,汇编)实现的本地方法。一般用于JNI中。 
public class testdll  
{  
static  
{  
System.loadLibrary("test");  
}  
    public native static int get();  
    public native static void set(int i);  
    public static void main(String[] args)  
    {  
testdll test = new testdll();  
test.set(10);  
System.out.println(test.get());  
}  
}  
test.dll可以用VC写,例如, 
-----testdll.h----- 
/* DO NOT EDIT THIS FILE - it is machine generated */ 
#include  <jni.h > 
/* Header for class testdll */ 
#ifndef _Included_testdll 
#define _Included_testdll 
#ifdef __cplusplus 
extern "C" { 
#endif 
/* 
* Class:     testdll 
* Method:    get 
* Signature: ()I 
*/ 
JNIEXPORT jint JNICALL Java_testdll_get 
  (JNIEnv *, jclass); 
/* 
* Class:     testdll 
* Method:    set 
* Signature: (I)V 
*/ 
JNIEXPORT void JNICALL Java_testdll_set 
  (JNIEnv *, jclass, jint); 
#ifdef __cplusplus 

#endif 
#endif 
-------testdll.c----------- 
#include "testdll.h"  
int i = 0;  
JNIEXPORT jint JNICALL Java_testdll_get (JNIEnv *, jclass)  
{  
return i;  
}  
JNIEXPORT void JNICALL Java_testdll_set (JNIEnv *, jclass, jint j)  
{  
i = j;  

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

恐怕比较一下volatile和synchronized的不同是最容易解释清楚的。volatile是变量修饰符,而synchronized则作用于一段代码或方法;看如下三句get代码:

  1. int i1;              int geti1() {return i1;}
  2. volatile int i2;  int geti2() {return i2;}
  3. int i3;              synchronized int geti3() {return i3;}

  geti1()得到存储在当前线程中i1的数值。多个线程有多个i1变量拷贝,而且这些i1之间可以互不相同。换句话说,另一个线程可能已经改变了它线程内的i1值,而这个值可以和当前线程中的i1值不相同。事实上,Java有个思想叫“主”内存区域,这里存放了变量目前的“准确值”。每个线程可以有它自己的变量拷贝,而这个变量拷贝值可以和“主”内存区域里存放的不同。因此实际上存在一种可能:“主”内存区域里的i1值是1,线程1里的i1值是2,线程2里的i1值是3——这在线程1和线程2都改变了它们各自的i1值,而且这个改变还没来得及传递给“主”内存区域或其他线程时就会发生。
  而geti2()得到的是“主”内存区域的i2数值。用volatile修饰后的变量不允许有不同于“主”内存区域的变量拷贝。换句话说,一个变量经volatile修饰后在所有线程中必须是同步的;任何线程中改变了它的值,所有其他线程立即获取到了相同的值。理所当然的,volatile修饰的变量存取时比一般变量消耗的资源要多一点,因为线程有它自己的变量拷贝更为高效。
  既然volatile关键字已经实现了线程间数据同步,又要synchronized干什么呢?呵呵,它们之间有两点不同。首先,synchronized获得并释放监视器——如果两个线程使用了同一个对象锁,监视器能强制保证代码块同时只被一个线程所执行——这是众所周知的事实。但是,synchronized也同步内存:事实上,synchronized在“主”内存区域同步整个线程的内存。因此,执行geti3()方法做了如下几步:
1. 线程请求获得监视this对象的对象锁(假设未被锁,否则线程等待直到锁释放)
2. 线程内存的数据被消除,从“主”内存区域中读入(Java虚拟机能优化此步。。。[后面的不知道怎么表达,汗])
3. 代码块被执行
4. 对于变量的任何改变现在可以安全地写到“主”内存区域中(不过geti3()方法不会改变变量值)
5. 线程释放监视this对象的对象锁
  因此volatile只是在线程内存和“主”内存间同步某个变量的值,而synchronized通过锁定和解锁某个监视器同步所有变量的值。显然synchronized要比volatile消耗更多资源。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值