實現(xiàn)多個線程調(diào)用同一個方法時,為避免數(shù)據(jù)出現(xiàn)交叉的情況,使用synchronized關(guān)鍵字進行同步
雖然在賦值時進行了同步,但在取值時有可能出現(xiàn)一些意想不到的意外,這種情況就是臟讀。發(fā)生臟讀的情況是在讀取實例變量時,此值已經(jīng)被其他線程更改過了。
/**
* @author wuyoushan
* @date 2017/4/10.
*/
public class PublicVar {
public String username="A";
public String password="AA";
synchronized public void setValue(String username,String password){
try{
this.username=username;
Thread.sleep(5000);
this.password=password;
System.out.println("setValue method thread name="+
Thread.currentThread().getName()+" username="+
username+" password="+password);
}catch (InterruptedException e){
e.printStackTrace();
}
}
public void getValue(){
System.out.println("getValue method thread name="+
Thread.currentThread().getName()+" username="+
username+" password="+password);
}
}
/**
* @author wuyoushan
* @date 2017/4/4.
*/
public class ThreadA extends Thread{
private PublicVar publicVar;
public ThreadA(PublicVar publicVar){
super();
this.publicVar=publicVar;
}
@Override
public void run() {
super.run();
publicVar.setValue("B","BB");
}
}
/**
* @author wuyoushan
* @date 2017/3/14.
*/
public class Test {
public static void main(String[] args) {
try{
PublicVar publicVar=new PublicVar();
ThreadA thread=new ThreadA(publicVar);
thread.start();
Thread.sleep(200);
publicVar.getValue();
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
程序的運行結(jié)果為:
getValue method thread name=main username=B password=AA
setValue method thread name=Thread-0 username=B password=BB
出現(xiàn)臟讀是因為public void getValue()方法并不是同步的,所以可以在任意時候進行調(diào)用。解決方法當(dāng)然就是加上同步synchronized關(guān)鍵字,代碼如下:
/**
* @author wuyoushan
* @date 2017/4/10.
*/
public class PublicVar {
public String username="A";
public String password="AA";
synchronized public void setValue(String username,String password){
try{
this.username=username;
Thread.sleep(5000);
this.password=password;
System.out.println("setValue method thread name="+
Thread.currentThread().getName()+" username="+
username+" password="+password);
}catch (InterruptedException e){
e.printStackTrace();
}
}
synchronized public void getValue(){
System.out.println("getValue method thread name="+
Thread.currentThread().getName()+" username="+
username+" password="+password);
}
}
程序的運行結(jié)果為:
setValue method thread name=Thread-0 username=B password=BB
getValue method thread name=main username=B password=BB
可見,方法setValue()和getValue()被依次執(zhí)行。通過這個案例不僅要知道臟讀是通過synchronized關(guān)鍵字解決的,還要知道如下內(nèi)容:
當(dāng)A線程調(diào)用anyObject對象加入synchronized關(guān)鍵字的X方法時,A線程就獲得了X方法的鎖,更準(zhǔn)確的講,是獲取了對象的鎖,所以其他線程必須等A線程執(zhí)行完畢才可以調(diào)用X方法,但B線程可以隨意調(diào)用其他的非synchronized同步方法
當(dāng)A線程調(diào)用anyObject對象加入synchronized關(guān)鍵字的X方法時,A線程就獲得了X方法的所在對象的鎖,所以其他線程必須等A線程執(zhí)行完畢才可以調(diào)用X方法,而B線程如果調(diào)用聲明了synchronized關(guān)鍵字的非X方法時,必須等A線程將X方法執(zhí)行完,也就是釋放對象鎖后才可以調(diào)用。這時A線程已經(jīng)執(zhí)行了一個完整的任務(wù),也就是說username和password這兩個實例變量已經(jīng)同時被賦值,不存在臟讀的基本環(huán)境
摘選自 java多線程核心編程技術(shù)-2.1.5