008.多線程-synchronized鎖

版權聲明:本文爲博主原創文章,允許轉載,請標明出處。

爲了解決線程安全問題, 我們的做法是:不要讓多個線程同時對一個全局變量作寫的操作。

常用的做法就是加鎖,來實現線程的同步。 自動鎖synchronized和手動鎖lock。 由於synchronized不需要手動釋放鎖,拋出異常也可自動釋放鎖。 後面將會介紹lock鎖。

一個線程拿到鎖後,其他線程則只能排隊,等待鎖的釋放。 代碼執行完畢或者程序拋出異常,鎖均會被釋放。


synchronized 代碼塊

    /**
     * 相當於同步函數
     */
    public void test1_() {
        synchronized (this) {
            for (int i = 1; i < 500; i++) {
                System.out.println("test1_..." + i);
            }
        }
    }
    /**
     * 相當於靜態同步函數
     */
    public void test1s_() {
        synchronized (Test.class) {
            for (int i = 1; i < 500; i++) {
                System.out.println("test1s_..." + i);
            }
        }
    }

	synchronized(lock-object){
	}

括號後面要跟一個對象, 這個對象充當鎖的作用。 Java中,全部都是對象,不相同的常量字符串也是不同的對象。


同步函數: (實例對象鎖)

在方法上修飾synchronized

    public synchronized void test1() {
        for (int i = 1; i < 500; i++) {
            System.out.println("test1..." + i);
        }
    }

靜態同步函數: (類對象鎖) 方法上加上static關鍵字,使用synchronized 關鍵字修飾 或者使用 類.class 文件。

    public static synchronized void test1s() {
        for (int i = 1; i < 500; i++) {
            System.out.println("test1..." + i);
        }
    }
    public void test1s_() {
        synchronized (Test.class) {
            for (int i = 1; i < 500; i++) {
                System.out.println("test1s_..." + i);
            }
        }
    }

若類對象被鎖,則類對象的所有同步方法全部被鎖。 若實例對象被鎖,則該實例對象的所有同步方法全部被鎖。 不是同一個對象,實例對象鎖沒有約束。


synchronized代碼塊的優勢:

  1. 只對需要同步的代碼進行同步
  2. 與wait() 、notify() 、notifyAll() 一起使用時,比較方便
package cn.qbz.thread;

public class WaitNotifyTest {


    public static void main(String[] args) {
        new Thread(new Produce()).start();
        new Thread(new Consumer()).start();
    }
}

class Produce implements Runnable {

    public void run() {
        int count = 5;
        while (count > 0) {
            synchronized ("lock-object") { //此處可以鎖定任何對象,只要鎖定Produce和Consumer中的對象一樣
                System.out.println("A");
                count--;
                "lock-object".notify();// 喚醒因爲調用對象的wait()而等待的線程

                if (count > 0) {
                    try {
                        "lock-object".wait();//釋放本線程的對象鎖,釋放CPU
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

    }

}

class Consumer implements Runnable {

    public void run() {
        int count = 5;
        while (count > 0) {
            synchronized ("lock-object") {

                System.out.println("B");
                count--;
                "lock-object".notify(); // 喚醒因爲調用對象的wait()而等待的線程

                if (count > 0) {
                    try {
                        "lock-object".wait(); //釋放本線程的對象鎖,釋放CPU
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

wait(): 釋放佔有的對象鎖,線程進入等待池,釋放cpu。 其他正在等待的線程即可搶佔此鎖,獲得鎖的線程即可運行程序。 sleep(): 線程休眠,休眠期間,釋放cpu,但不釋放佔有的對象鎖。 即:休眠期間,其他線程依然無法進入此同步代碼塊內。 notify(): 喚醒因爲調用對象的wait()而等待的線程。 調用notify()後,並不會立即釋放鎖, 而是直到synchronized代碼塊中全部執行完畢,才釋放鎖。 JVM會在等待的線程中調度一個線程去獲得此鎖,執行代碼。 notifyAll() 喚醒所有等待的線程。


notify與notifyAll 鎖池: 假設線程A已經擁有了某個對象鎖(非類鎖), 此時想獲取此對象鎖的其他線程,將進入此對象的鎖池中, 參與下次鎖的競爭。 等待池: 假設線程A調用了對象鎖的wait()方法, 線程A會釋放該對象鎖,並進入此對象的等待池中。 等待池中的線程不會參與對象鎖的競爭。 notify調用後,只會將等待池中的一個隨機線程移到鎖池中。 notifyAll調用後,會將全部線程移到鎖池中。

notify有一定機率造成死鎖。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章