標籤:
Volatile修飾的成員變數在每次被線程訪問時,都強迫從主記憶體中重讀該成員變數的值。而且,當成員變數發生變化時,強迫線程將變化值回寫到主記憶體。這樣在任何時刻,兩個不同的線程總是看到某個成員變數的同一個值。
Java語言規範中指出:為了獲得最佳速度,允許線程儲存共用成員變數的私人拷貝,而且只當線程進入或者離開同步代碼塊時才與共用成員變數的原始值對比。
這樣當多個線程同時與某個對象互動時,就必須要注意到要讓線程及時的得到共用成員變數的變化。
而volatile關鍵字就是提示VM:對於這個成員變數不能儲存它的私人拷貝,而應直接與共用成員變數互動。
使用建議:在兩個或者更多的線程訪問的成員變數上使用volatile。當要訪問的變數已在synchronized代碼塊中,或者為常量時,不必使用。由於使用volatile屏蔽掉了VM中必要的代碼最佳化,所以在效率上比較低,因此一定在必要時才使用此關鍵字。
java關鍵字Transient
Java的serialization提供了一種持久化對象執行個體的機制。當持久化對象時,可能有一個特殊的對象資料成員,我們不想用serialization機制來儲存它。為了在一個特定對象的一個域上關閉serialization,可以在這個域前加上關鍵字transient。
transient是Java語言的關鍵字,用來表示一個域不是該對象序列化的一部分。當一個對象被序列化的時候,transient型變數的值不包括在序列化的表示中,然而非transient型的變數是被包括進去的。
注意static變數也是可以序列化的
首先,讓我們看一些Java serialization的代碼:
import java.util.Date;public class LoggingInfo implements java.io.Serializable { private Date loggingDate = new Date(); private String uid; private transient String pwd; LoggingInfo(String user, String password) { uid = user; pwd = password; } public String toString() { String password=null; if(pwd == null) { password = "NOT SET"; } else { password = pwd; } return"logon info: \n " + "user: " + uid + "\n logging date : " + loggingDate.toString() + "\n password: " + password; } }
現在我們建立一個這個類的執行個體,並且序列化(serialize)它 ,然後將這個序列化對象寫如磁碟。
package com.wms.test;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;public class TestTransient implements java.io.Serializable { public static void main(String[] args) { LoggingInfo logInfo = new LoggingInfo("MIKE", "MECHANICS"); System.out.println(logInfo.toString()); try { ObjectOutputStream o = new ObjectOutputStream( new FileOutputStream("logInfo.out")); o.writeObject(logInfo); o.close(); } catch(Exception e) { //deal with exception } //To read the object back, we can write try { ObjectInputStream in =new ObjectInputStream( new FileInputStream("logInfo.out")); LoggingInfo logInfoIn = (LoggingInfo)in.readObject(); System.out.println(logInfoIn.toString()); } catch(Exception e) { //deal with exception } }}
輸出結果:
logon info: user: MIKE logging date : Wed Apr 13 22:13:44 CST 2016 password: MECHANICSlogon info: user: MIKE logging date : Wed Apr 13 22:13:44 CST 2016 password: NOT SET
如果我們運行這段代碼,我們會注意到從磁碟中讀回(read)的對象列印password為"NOT SET"。這是當我們定義pwd為transient時,所期望的正確結果。 現在,讓我們來看一下粗心對待transient域可能引起的潛在問題。假設我們修改了類定義,提供給transient域一個預設值,代碼如下:
public class GuestLoggingInfo implements java.io.Serializable { private Date loggingDate = new Date(); private String uid; private transient String pwd; GuestLoggingInfo() { uid = "guest"; pwd = "guest"; } public String toString() { // same as above }} 現在,如果我們序列化GuestLoggingInfo的一個執行個體,將它寫入磁碟,並且再將它從磁碟中讀出,我們仍然看到讀回的對象列印password 為 "NOT SET"。當從磁碟中讀出某個類的執行個體時,實際上並不會執行這個類的建構函式,而是載入了一個該類對象的持久化狀態,並將這個狀態賦值給該類的另一個對象。
Java transient關鍵字