《Java併發程式設計的藝術》第二章——Java併發機制的底層實現
阿新 • • 發佈:2019-02-14
Java併發機制的底層實現原理
知識點:
,當CPU修改共享變數後(其實只是修改的共享變數的副本),需要寫回到記憶體中,而寫回到記憶體中這個操作,時機是不確定的,所以就可能造成該共享變數已經修改(但並未寫回記憶體),但其他快取記憶體中仍然儲存著舊值的副本的情況。
2.synchronized的實現原理及應用
synchronized是多執行緒併發程式設計中的元老,亦可稱為”重量級鎖“。
以下例子展示synchronized的用法:
例項一:
執行結果:
通過結果可以看到執行緒1和2在執行同步程式碼時,執行緒1先獲取到鎖,直到3秒後釋放鎖,執行緒2才可以獲取到鎖並執行。 例項二:
Synchronized(this)表示在執行該程式碼塊之前需要獲取到物件鎖。線上程1獲取到物件鎖後,其他執行緒仍然可訪問其他未同步的程式碼。 例項三:
執行結果:
例項一和二的鎖,都是物件鎖,即每個執行緒中使用的都是同一個Sync物件,若在每個執行緒中宣告不同的Sync物件,則不會出現執行緒阻塞等待鎖的情況,因為每個執行緒獲取到及需要獲取的物件鎖並不是同一個。但例項三種同步程式碼塊中需要獲取“類鎖”,即使在每個執行緒中宣告不同的Sync物件,也避免不了鎖的等待。因為該鎖是“屬於類的”,是同一個。 總結synchronized使用形式: a),對於普通方法,鎖是當前物件。 b),對於靜態同步方法,鎖是當前類的Class物件。 c),對於同步方法塊,鎖是synchronized括號裡配置的物件。
但對於不同的鎖及其狀態而言,4個位元組不足以表示其資訊,所以按照鎖標誌位的不同,來儲存不同的資訊。
a),輕量級鎖加鎖: 執行緒在執行同步程式碼塊之前,JVM會先在當前執行緒的棧幀中建立用於儲存鎖記錄的空間,並將物件頭中的Mark Word複製到鎖記錄中(DIsplaced Mark Word)。然後執行緒嘗試使用CAS將物件頭中的Mark Word替換為指向鎖記錄的指標。如果成功,則獲得鎖。如果失敗,則表示有其他執行緒競爭鎖,當前執行緒嘗試使用自旋來獲取鎖。 b),輕量級鎖解鎖: 在解鎖時,會使用原子的CAS操作將DIsplaced Mark Word替換回物件頭,如果成功,則表示沒有競爭發生。如果失敗,則表示當前所存在競爭,鎖會膨脹為重量級鎖。競爭該鎖的執行緒全部會阻塞,知道當前執行緒釋放該鎖。 鎖膨脹為重量級鎖流程圖如下:
c),鎖的優缺點對比
3.原子操作的實現原理 原子本意是“不能被進一步分割的最小粒子”,而原子操作意為“不可被中斷的一個或一個系列操作”。
- volatile的應用
- synchronized的實現原理及應用
- 原子操作的實現原理
- 何為“可見性”?
- 為什麼需要“可見性”?
- volatile 的作用及原理?
- volatile的使用優化
package com.lipeng.second;
import java.util.concurrent.TimeUnit;
/**
* 使用Synchronized修飾方法,同一時間只能有一個執行緒訪問被同步的程式碼
* SyncDemo1-Thread-1、SyncDemo1-Thread-2訪問同步程式碼
* SyncDemo1-Thread-3、SyncDemo1-Thread-4訪問非同步程式碼
* @author promi
*
*/
public class SyncDemo1 {
static Sync1 sync=new Sync1();
public static void main(String[] args) {
Thread thread1=new Thread(new Runnable() {
@Override
public void run() {
sync.syncAction();
}
},"SyncDemo1-Thread-1");
Thread thread2=new Thread(new Runnable() {
@Override
public void run() {
sync.syncAction();
}
},"SyncDemo1-Thread-2");
Thread thread3=new Thread(new Runnable() {
@Override
public void run() {
sync.noSyncAction();
}
},"SyncDemo1-Thread-3");
Thread thread4=new Thread(new Runnable() {
@Override
public void run() {
sync.noSyncAction();
}
},"SyncDemo1-Thread-4");
thread1.start();
thread2.start();
thread3.start();
thread4.start();
}
}
class Sync1{
public synchronized void syncAction(){
System.out.println(Thread.currentThread().getName()+" 執行syncAction方法 ,TimeStrap:"+System.currentTimeMillis());
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void noSyncAction(){
System.out.println(Thread.currentThread().getName()+"執行noSyncAction方法,TimeStrap:"+System.currentTimeMillis());
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
執行結果:
通過結果可以看到執行緒1和2在執行同步程式碼時,執行緒1先獲取到鎖,直到3秒後釋放鎖,執行緒2才可以獲取到鎖並執行。 例項二:
package com.lipeng.second;
import java.util.concurrent.TimeUnit;
/**
* 使用synchronized封裝程式碼塊獲取"物件鎖"
* SyncDemo1-Thread-1、SyncDemo1-Thread-2訪問同步程式碼
* SyncDemo1-Thread-3、SyncDemo1-Thread-4訪問非同步程式碼
* @author promi
*
*/
public class SyncDemo2 {
static Sync2 sync=new Sync2();
public static void main(String[] args) {
Thread thread1=new Thread(new Runnable() {
@Override
public void run() {
sync.syncAction();
}
},"SyncDemo2-Thread-1");
Thread thread2=new Thread(new Runnable() {
@Override
public void run() {
sync.syncAction();
}
},"SyncDemo2-Thread-2");
Thread thread3=new Thread(new Runnable() {
@Override
public void run() {
sync.noSyncAction();
}
},"SyncDemo2-Thread-3");
Thread thread4=new Thread(new Runnable() {
@Override
public void run() {
sync.noSyncAction();
}
},"SyncDemo2-Thread-4");
thread1.start();
thread2.start();
thread3.start();
thread4.start();
}
}
class Sync2{
public void syncAction(){
synchronized(this){
System.out.println(Thread.currentThread().getName()+" 執行syncAction方法 ,TimeStrap:"+System.currentTimeMillis());
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void noSyncAction(){
System.out.println(Thread.currentThread().getName()+" 執行noSyncAction方法 ,TimeStrap:"+System.currentTimeMillis());
}
}
執行結果:
Synchronized(this)表示在執行該程式碼塊之前需要獲取到物件鎖。線上程1獲取到物件鎖後,其他執行緒仍然可訪問其他未同步的程式碼。 例項三:
package com.lipeng.second;
import java.util.concurrent.TimeUnit;
/**
* 使用synchronized封裝程式碼塊獲取"類鎖"
* SyncDemo1-Thread-1、SyncDemo1-Thread-2訪問同步程式碼
* SyncDemo1-Thread-3、SyncDemo1-Thread-4訪問非同步程式碼
* @author promi
*
*/
public class SyncDemo3 {
public static void main(String[] args) {
Thread thread1=new Thread(new Runnable() {
@Override
public void run() {
Sync3 sync=new Sync3();
sync.syncAction();
}
},"SyncDemo3-Thread-1");
Thread thread2=new Thread(new Runnable() {
@Override
public void run() {
Sync3 sync=new Sync3();
sync.syncAction();
}
},"SyncDemo3-Thread-2");
Thread thread3=new Thread(new Runnable() {
@Override
public void run() {
Sync3 sync=new Sync3();
sync.noSyncAction();
}
},"SyncDemo3-Thread-3");
Thread thread4=new Thread(new Runnable() {
@Override
public void run() {
Sync3 sync=new Sync3();
sync.noSyncAction();
}
},"SyncDemo3-Thread-4");
thread1.start();
thread2.start();
thread3.start();
thread4.start();
}
}
class Sync3{
public void syncAction(){
synchronized(Sync3.class){
System.out.println(Thread.currentThread().getName()+" 執行syncAction方法 ,TimeStrap:"+System.currentTimeMillis());
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void noSyncAction(){
System.out.println(Thread.currentThread().getName()+" 執行noSyncAction方法 ,TimeStrap:"+System.currentTimeMillis());
}
}
執行結果:
例項一和二的鎖,都是物件鎖,即每個執行緒中使用的都是同一個Sync物件,若在每個執行緒中宣告不同的Sync物件,則不會出現執行緒阻塞等待鎖的情況,因為每個執行緒獲取到及需要獲取的物件鎖並不是同一個。但例項三種同步程式碼塊中需要獲取“類鎖”,即使在每個執行緒中宣告不同的Sync物件,也避免不了鎖的等待。因為該鎖是“屬於類的”,是同一個。 總結synchronized使用形式: a),對於普通方法,鎖是當前物件。 b),對於靜態同步方法,鎖是當前類的Class物件。 c),對於同步方法塊,鎖是synchronized括號裡配置的物件。
- 那麼synchronized在JVM裡是怎麼實現的呢?
- synchronized用的鎖是存在哪裡的?
- 物件頭的結構?
但對於不同的鎖及其狀態而言,4個位元組不足以表示其資訊,所以按照鎖標誌位的不同,來儲存不同的資訊。
- 偏向鎖
- 輕量級鎖
a),輕量級鎖加鎖: 執行緒在執行同步程式碼塊之前,JVM會先在當前執行緒的棧幀中建立用於儲存鎖記錄的空間,並將物件頭中的Mark Word複製到鎖記錄中(DIsplaced Mark Word)。然後執行緒嘗試使用CAS將物件頭中的Mark Word替換為指向鎖記錄的指標。如果成功,則獲得鎖。如果失敗,則表示有其他執行緒競爭鎖,當前執行緒嘗試使用自旋來獲取鎖。 b),輕量級鎖解鎖: 在解鎖時,會使用原子的CAS操作將DIsplaced Mark Word替換回物件頭,如果成功,則表示沒有競爭發生。如果失敗,則表示當前所存在競爭,鎖會膨脹為重量級鎖。競爭該鎖的執行緒全部會阻塞,知道當前執行緒釋放該鎖。 鎖膨脹為重量級鎖流程圖如下:
c),鎖的優缺點對比
3.原子操作的實現原理 原子本意是“不能被進一步分割的最小粒子”,而原子操作意為“不可被中斷的一個或一個系列操作”。
- 處理器如何實現原子操作?
- Java中如何實現原子操作?