1. 程式人生 > 其它 >Java開發基礎之Synchronized的三種應用方式

Java開發基礎之Synchronized的三種應用方式

synchronized的三種應用方式

synchronized關鍵字最主要有以下3種應用方式,下面分別介紹

  • 修飾例項方法,作用於當前例項加鎖,進入同步程式碼前要獲得當前例項的鎖
  • 修飾靜態方法,作用於當前類物件加鎖,進入同步程式碼前要獲得當前類物件的鎖
  • 修飾程式碼塊,指定加鎖物件,對給定物件加鎖,進入同步程式碼塊前要獲得給定物件的鎖

1.synchronized作用於例項方法

我們設定類變數static為共享資源, 然後多個執行緒去修改。修改的含義是:先讀取,計算,再寫入。那麼這個過程就不是原子的,多個執行緒操作就會出現共享資源爭搶問題。

我們在例項方法上新增synchronized,那麼,同一個例項執行本方法時,搶到鎖到可以執行。

public class AccountingSync implements Runnable{
//共享資源(臨界資源)
static int i=0;


/**
* synchronized 修飾例項方法
*/
public synchronized void increase(){
i++;
}
@Override
public void run() {
for(int j=0;j<1000000;j++){
increase();
}
}
public static void main(String[] args) throws InterruptedException {
AccountingSync instance=new AccountingSync();
Thread t1=new Thread(instance);
Thread t2=new Thread(instance);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
/**
* 輸出結果:
* 2000000
*/
}

上述程式碼中,開啟兩個執行緒去操作共享變數,兩個執行緒執行的是同一個例項物件。如果不新增synchronized,其中i++不是原子操作,該操作先讀取值,然後再寫入一個新值。如果兩個執行緒都讀取了i=5,然後執行緒1寫入i=6.執行緒2後寫入,但也是寫入i=6, 並不是我們期望的i=7.

新增synchronized修飾後,執行緒安全,執行緒必須獲取到這個實力到鎖才能執行讀取和寫入。

注意,我們synchronized修飾到是類方法,鎖的是例項,當多個執行緒操作不同例項時,會使用不同例項的鎖,就無法保證修改static變數的有序性了。

public class AccountingSyncBad implements Runnable{
static int i=0;
public synchronized void increase(){
i++;
}
@Override
public void run() {
for(int j=0;j<1000000;j++){
increase();
}
}
public static void main(String[] args) throws InterruptedException {
//new新例項
Thread t1=new Thread(new AccountingSyncBad());
//new新例項
Thread t2=new Thread(new AccountingSyncBad());
t1.start();
t2.start();
//join含義:當前執行緒A等待thread執行緒終止之後才能從thread.join()返回
t1.join();
t2.join();
System.out.println(i);
}
}

上述程式碼,兩個執行緒持有不同的物件instance,也就是使用不同的鎖, 也就不會互斥訪問共享資源,就會出現執行緒安全問題。

2.synchronized作用於靜態方法

synchronized作用於靜態方法時,鎖就是當前類到class物件鎖。由於靜態成員變數不專屬於任何一個例項物件,是類成員,因此通過class物件鎖可以控制靜態成員的併發操作。

3.synchronized同步程式碼塊

除了使用關鍵字修飾例項方法和靜態方法外,還可以使用同步程式碼塊。

public class AccountingSync implements Runnable{
static AccountingSync instance=new AccountingSync();
static int i=0;
@Override
public void run() {
//省略其他耗時操作....
//使用同步程式碼塊對變數i進行同步操作,鎖物件為instance
synchronized(instance){
for(int j=0;j<1000000;j++){
i++;
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread(instance);
Thread t2=new Thread(instance);
t1.start();t2.start();
t1.join();t2.join();
System.out.println(i);
}
}

上述程式碼,將synchronized作用於一個給定的例項物件instance, 即當前例項物件就是鎖物件,每次當執行緒進入synchronized包裹到程式碼塊時,就會要求當前執行緒持有instance例項物件鎖,如果當前有其他執行緒正持有該物件鎖,那麼新到到執行緒就必須等待,這樣也就保證了每次只有一個執行緒執行i++操作。當然, 還可以使用this或者class

//this,當前例項物件鎖
synchronized(this){
for(int j=0;j<1000000;j++){
i++;
}
}


//class物件鎖
synchronized(AccountingSync.class){
for(int j=0;j<1000000;j++){
i++;
}
}

相關​java培訓​開發技術知識,關注我,有更多精彩內容與您分享!