[轉載]關於native,transient,volatile,synchronized四個關鍵字的使用

轉載:http://www.blogjava.net/bacoo/archive/2007/12/20/169172.html
native
當你需要調用本地程序的時候
transient
你的類實現了java.io.Serializable而你又不想保存某個字段的時候
volatile
這個字段會被其他線程(直接或者間接)訪問到,而你想保證每個線程都能得到最新的數據 (性能上肯定有損耗的,爲了安全犧牲性能的事情多着去了)

volatile
原CSDN有一篇介紹過,現貼回來。
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);    
    }    
}   

transient
transient是一個變量修飾符,標記爲transient的變量,在對一個對象進行序列化時,這些變量狀態不會被序列化。
例如,假設某個類的成員變量是transient,那麼當通過ObjectOutputStream把這個類的某個實例保存到磁盤上時,實際上transient變量的值是不會保存的。
當對象序列化的保存在存儲器上時,不希望有些字段數據被保存,爲了保證安全性,可以把這些字段聲明爲transient。 要更明白,可以看一些序列化的內容。

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代碼:

int i1; int geti1() {return i1;}
volatile int i2; int geti2() {return i2;}
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消耗更多資源。

附英文原文:
What does volatile do?

This is probably best explained by comparing the effects that volatile and synchronized have on a method. volatile is a field modifier, while synchronized modifies code blocks and methods. So we can specify three variations of a simple accessor using those two keywords:

int i1; int geti1() {return i1;}
volatile int i2; int geti2() {return i2;}
int i3; synchronized int geti3() {return i3;}
geti1() accesses the value currently stored in i1 in the current thread. Threads can have local copies of variables, and the data does not have to be the same as the data held in other threads. In particular, another thread may have updated i1 in it’s thread, but the value in the current thread could be different from that updated value. In fact Java has the idea of a “main” memory, and this is the memory that holds the current “correct” value for variables. Threads can have their own copy of data for variables, and the thread copy can be different from the “main” memory. So in fact, it is possible for the “main” memory to have a value of 1 for i1, for thread1 to have a value of 2 for i1 and for thread2 to have a value of 3 for i1 if thread1 and thread2 have both updated i1 but those updated value has not yet been propagated to “main” memory or other threads.

On the other hand, geti2() effectively accesses the value of i2 from “main” memory. A volatile variable is not allowed to have a local copy of a variable that is different from the value currently held in “main” memory. Effectively, a variable declared volatile must have it’s data synchronized across all threads, so that whenever you access or update the variable in any thread, all other threads immediately see the same value. Of course, it is likely that volatile variables have a higher access and update overhead than “plain” variables, since the reason threads can have their own copy of data is for better efficiency.

Well if volatile already synchronizes data across threads, what is synchronized for? Well there are two differences. Firstly synchronized obtains and releases locks on monitors which can force only one thread at a time to execute a code block, if both threads use the same monitor (effectively the same object lock). That’s the fairly well known aspect to synchronized. But synchronized also synchronizes memory. In fact synchronized synchronizes the whole of thread memory with “main” memory. So executing geti3() does the following:

  1. The thread acquires the lock on the monitor for object this (assuming the monitor is unlocked, otherwise the thread waits until the monitor is unlocked).
  2. The thread memory flushes all its variables, i.e. it has all of its variables effectively read from “main” memory (JVMs can use dirty sets to optimize this so that only “dirty” variables are flushed, but conceptually this is the same. See section 17.9 of the Java language specification).
  3. The code block is executed (in this case setting the return value to the current value of i3, which may have just been reset from “main” memory).
  4. (Any changes to variables would normally now be written out to “main” memory, but for geti3() we have no changes.)
  5. The thread releases the lock on the monitor for object this.

So where volatile only synchronizes the value of one variable between thread memory and “main” memory, synchronized synchronizes the value of all variables between thread memory and “main” memory, and locks and releases a monitor to boot. Clearly synchronized is likely to have more overhead than volatile.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章