1.問題(爲什麼要實現原子性(同步)與及可見性)?
在高併發開發環境中,如果不控制數據的原子性與及可見性,可能會導致數據的不一致性,也可能導致系統阻塞,嚴重破壞了系統的穩定性。
原子性:即保證數據在同一時段只允許一個線程進行操作
可見性:即保證線程在工作內存中修改完成後,在釋放鎖之前,需要把數據寫回到主內存當中。
2.代碼實現與及驗證
/**
* 資源類
*/
class MyDataSynchronized {
//共享變量
private int number = 1;
/**
* 無鎖 寫操作,不保證原子性
*/
public void notLockWrite() {
if (notLockRead() == 2) {
System.out.println(Thread.currentThread().getName() + "\t " + "無需修改!");
return;
}
System.out.println(Thread.currentThread().getName() + "\t " + "修改!");
this.number = 2;
}
/**
* 無鎖 讀操作,不保證可見性
*/
public int notLockRead() {
return this.number;
}
}
/**
* synchronized能夠實現原子性(同步)、可見性
*/
public class SynchronizedDemo {
public static void main(String[] args) {
notSync();
}
/**
* 無鎖
*/
private static void notSync() {
MyDataSynchronized myDataSynchronized = new MyDataSynchronized();
//n個線程讀
for (int i = 1; i < 3; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "\t start!");
//監測number值是否改變,改變即及時退出
while (myDataSynchronized.notLockRead() == 1) {
}
System.out.println(Thread.currentThread().getName() + "\t end : " + myDataSynchronized.notLockRead());
}, "Read "+String.valueOf(i)).start();
}
//n個線程寫
for (int i = 1; i < 6; i++) {
new Thread(() -> {
//保證讀線程首先執行讀取操作,也保證所有寫線程能同時進行寫操作
try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}
myDataSynchronized.notLockWrite();
}, "Write "+String.valueOf(i)).start();
}
}
}
驗證:將共享變量的值進行修改,其中兩個讀取線程,五個寫線程,爲了讀取線程先行運行,開啓寫線程時進行睡眠兩秒,也方便模擬同時寫操作。
預期結果:如果寫線程只能執行成功一次寫操作,讀取線程在寫操作完成後自行結束。
執行結果(線程多次修改,且讀取線程進入阻塞,未能達到預期效果)
原因:讀取線程進入阻塞,原因是修改線程修改的資源未寫回主內存當中,修改的資源對於其它線程是不可見的。
線程多次修改原因主要是寫線程同時讀到number==1,各自執行寫操作
修正:1.讀取操作加synchronized,寫操作不加。會導致多次修改
2.寫操作加synchronized,讀取操作不加。會導致讀取線程阻塞
正確修改:在讀與寫操作同時加鎖
/**
* 有鎖 寫操作,保證原子性
*/
public synchronized void useLockWrite() {
if (useLockRead() == 2) {
System.out.println(Thread.currentThread().getName() + "\t " + "無需修改!");
return;
}
System.out.println(Thread.currentThread().getName() + "\t " + "修改!");
this.number = 2;
}
/**
* 有鎖 讀操作,保證可見性
*/
public synchronized int useLockRead() {
return this.number;
}
執行結果(讀取線程正常完成,寫線程只有一次寫操作)
3.總結
synchronized關鍵字可實現多線程同步與及數據的可見性。
如上文章比較簡陋,若其中有不恰當的地方,希望大家一起分享一下自己的意見。共同學習,一起成長。