爲了解決線程併發的問題,在語言內部引入了 同步塊(synchronized) 和 volatile 關鍵字機制;
關鍵字:synchronized
同步塊:synchronized,所有加上synchronized的塊語句,在多線程訪問的時候,同一時刻只能有一個線程能夠用
synchronized 修飾的方法 或者 代碼塊。
作用:保證同一時刻最多隻有1個線程執行 被Synchronized
修飾的方法 / 代碼,其他線程 必須等待當前線程執行完該方法 / 代碼塊後才能執行該方法 / 代碼塊,解決多線程中的併發同步問題;
使用實例:
private static ThreadLocal<SimpleDateFormat> FORMAT_DATE_DB_THREADLOCAL = new ThreadLocal<SimpleDateFormat>() {
protected synchronized SimpleDateFormat initialValue() {
SimpleDateFormat sd = new SimpleDateFormat(
"yyyyMMdd");
sd.setLenient(false);
return sd;
}
};
關鍵字:volatile
作用:保證數據在多線程間的內存可見性;
在多線程環境下,某個共享變量如果被其中一個線程給修改了,其他線程能夠立即知道這個共享變量已經被修改了,當其他線程要讀取這個變量的時候,最終會去內存中讀取,而不是從自己的工作空間中讀取。
也就是說,使用volatile關鍵字後,會有如下效果:
1、每次對變量的修改,都會引起處理器緩存(工作內存)寫回到主存;
2、一個工作內存回寫到主存會導致其他線程的處理器緩存(工作內存)無效。
事例:
不使用volatile,執行出現死循環:
public class TestWithoutVolatile {
private static boolean bChanged;
public static void main(String[] args) throws InterruptedException {
new Thread() {
@Override
public void run() {
for (; ; ) {
if (bChanged == !bChanged) {
System.out.println("!=");
System.exit(0);
}
}
}
}.start();
Thread.sleep(1);
new Thread() {
@Override
public void run() {
for (; ; ) {
bChanged = !bChanged;
}
}
}.start();
}
}
使用volatile,程序輸出!=,然後馬上退出。
public class TestWithVolatile {
private static volatile boolean bChanged;
public static void main(String[] args) throws InterruptedException {
new Thread() {
@Override
public void run() {
for (; ; ) {
if (bChanged == !bChanged) {
System.out.println("!=");
System.exit(0);
}
}
}
}.start();
Thread.sleep(1);
new Thread() {
@Override
public void run() {
for (; ; ) {
bChanged = !bChanged;
}
}
}.start();
}
}
PS重要文章:https://www.jianshu.com/p/2ed498b43628 (詳解synchronized)