synchronized 總結

方法鎖形式: synchronized 修飾普通方法, 鎖對象默認爲 this ,也就是當前實例對象html

 

注意若是是 不一樣 類鎖的 不一樣 的同步代碼塊。狀況是有點特殊的,雖然也是有順序了。java

package com.my.com.my.sysnc;

/**
 * 同步鎖測試
 */
public class TestSynchronized implements  Runnable{

    static  TestSynchronized instance=new TestSynchronized();

    Object lock1=new Object();
    Object lock2=new Object();

    @Override
    public void run() {



        synchronized (lock1){
            System.out.println("lock1對象鎖代碼塊,線程名"+Thread.currentThread().getName());

            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread().getName()+"結束lock1");
        }

        synchronized (lock2){
            System.out.println("lock2對象鎖代碼塊,線程名"+Thread.currentThread().getName());

            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread().getName()+"結束lock2");
        }



    }

   


    public static void main(String[] args) {

        Thread thread1=new Thread(instance);
        Thread thread2=new Thread(instance);
        thread1.start();
        thread2.start();

    }

}



結果  即 ,在run 方法裏面, 當 第一個線程執行完 第一個同步代碼塊的時候,在開始執行第一個代碼塊的時候。
此時 第二個線程會去執行第一個同步代碼塊。 這樣就有 點並行執行了
:

lock1對象鎖代碼塊,線程名Thread-0
Thread-0結束lock1
lock2對象鎖代碼塊,線程名Thread-0
lock1對象鎖代碼塊,線程名Thread-1
Thread-0結束lock2
Thread-1結束lock1
lock2對象鎖代碼塊,線程名Thread-1
Thread-1結束lock2

 

 

 

 

類鎖面試

 

 

 

若是是對象鎖的話,那麼不一樣的對象對於不一樣的線程來講,就不會起到 鎖的做用了。ide

若是是 類鎖的話,那麼就能夠作到 不一樣的對象對於不一樣的線程 起到 鎖的做用。函數

普通方法: 即非 靜態方法測試

 

拋出異常的適合, LOCK 鎖不會釋放鎖,而 synchronized  會釋放鎖this

 

面試題spa

1. 兩個線程同時訪問一個對象的同步方法:.net

可以鎖住,同一個實例線程

2. 兩個線程訪問的是兩個對象的同步方法。

由於不一樣的實例全部並無鎖的做用,鎖對象不是同一個

3. 兩個線程訪問的是 synchronized 的靜態方法

鎖的是 class對象, 鎖住 

4. 同時訪問同步方法與非同步方法

同步方法不能影響 非同步方法。 

同步方法與非同步方法能夠同時運行

 

5. 訪問同一個對象的不一樣的普通同步方法

方法不能同時的運行。 鎖的是同一個對象。受鎖的影響

例子:

package com.my.com.my.sysnc;

/**
 * 同步鎖測試
 */
public class TestSynchronized implements  Runnable{

    static  TestSynchronized instance=new TestSynchronized();

    @Override
    public void run() {

       if (Thread.currentThread().getName().equals("Thread-0")){
           test1();
       }else{
           test2();
       }


    }

    public  synchronized void test1(){
        System.out.println("對象鎖代碼塊"+Thread.currentThread().getName());

        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(Thread.currentThread().getName()+"結束線程");
    }

    public  synchronized void test2(){
        System.out.println("對象鎖代碼塊"+Thread.currentThread().getName());

        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(Thread.currentThread().getName()+"結束線程");
    }


    public static void main(String[] args) {

        Thread thread1=new Thread(instance);
        Thread thread2=new Thread(instance);
        thread1.start();
        thread2.start();

    }

}

 鎖的是同一個實例,競爭的是同一個實例,因此受到影響

可能的結果 :
對象鎖代碼塊Thread-1
Thread-1結束線程
對象鎖代碼塊Thread-0
Thread-0結束線程

 

6. 同時訪問靜態 synchronized 和 非靜態 synchronized 方法

鎖不其做用,兩個方法均可以一塊兒運行。

由於靜態鎖的是 class 對象,非靜態的 鎖的是 實例對象。是不同的

package com.my.com.my.sysnc;

/**
 * 同步鎖測試
 */
public class TestSynchronized implements  Runnable{

    static  TestSynchronized instance=new TestSynchronized();

    Object lock1=new Object();
    Object lock2=new Object();

    @Override
    public void run() {

       if (Thread.currentThread().getName().equals("Thread-0")){
           test1();
       }else{
           test2();
       }




    }

    public static   synchronized void test1(){
        System.out.println("靜態加鎖方法1,,對象鎖代碼塊"+Thread.currentThread().getName());

        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(Thread.currentThread().getName()+"結束線程");
    }

    public  synchronized void test2(){
        System.out.println("非靜態加鎖方法2,對象鎖代碼塊"+Thread.currentThread().getName());

        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(Thread.currentThread().getName()+"結束線程");
    }


    public static void main(String[] args) {

        Thread thread1=new Thread(instance);
        Thread thread2=new Thread(instance);
        thread1.start();
        thread2.start();

    }

}

結果:
非靜態加鎖方法2,對象鎖代碼塊Thread-1
靜態加鎖方法1,,對象鎖代碼塊Thread-0
Thread-0結束線程
Thread-1結束線程

 

 

7. 方法拋異常後,會釋放鎖

 

package com.my.com.my.sysnc;

/**
 * 同步鎖測試
 */
public class TestSynchronized implements  Runnable{

    static  TestSynchronized instance=new TestSynchronized();

    Object lock1=new Object();
    Object lock2=new Object();

    @Override
    public void run() {

       if (Thread.currentThread().getName().equals("Thread-0")){
           test1();
       }else{
           test2();
       }




    }

    public    synchronized void test1(){
        System.out.println("對象鎖代碼塊"+Thread.currentThread().getName());

        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    // 拋出異常後,釋放鎖,由JVM處理釋放鎖
       throw  new RuntimeException("拋出異常");
    }

    public  synchronized void test2(){
        System.out.println("對象鎖代碼塊"+Thread.currentThread().getName());

        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(Thread.currentThread().getName()+"結束線程");
    }


    public static void main(String[] args) {

        Thread thread1=new Thread(instance);
        Thread thread2=new Thread(instance);
        thread1.start();
        thread2.start();

    }

}


結果:
對象鎖代碼塊Thread-0
對象鎖代碼塊Thread-1
Exception in thread "Thread-0" java.lang.RuntimeException: 拋出異常
	at com.my.com.my.sysnc.TestSynchronized.test1(TestSynchronized.java:36)
	at com.my.com.my.sysnc.TestSynchronized.run(TestSynchronized.java:17)
	at java.lang.Thread.run(Thread.java:748)
Thread-1結束線程

 



性質

1, 可重入:避免死鎖,提高封裝性

指的是 同一線程的外層函數得到鎖以後,內層函數能夠之間再次獲取該鎖

 

同一個方法可重入(好比遞歸調用),

不要求同一個方法也能夠重入(好比同一個類中調用2個都是鎖的方法) ,

可重入不要求 是同一個類中的(子類的 重寫父類的加鎖方法,並調用父類的加鎖方法)

 

2,不可中斷 

 

 

 

 

 

 

3. 粒度

 

線程而非調用

 

 

原理

由於每個對象均可以做爲 鎖的部分。

因此每一個對象都有一個 內置鎖。 

線程在進入 synchronized 裏面以前,會獲取 synchronized 鎖的對象的 內置鎖。

當 執行完 synchronized 的 鎖的代碼後,會釋放 對應的對象的 內置鎖。

Java 對象的對象頭有一個 字段用來講明是否鎖住了,因此 synchronized  做用就是 根據這個字段來 鎖的

編譯執行的時候 ,JVM 經過   Java的 指令  monitorenter 進入 鎖(monitor計數器加1),  monitorexit 退出 鎖(monitor計數器減1)。

經過判斷  monitor 數字 判斷是否 是否能夠獲取鎖 。

注意雖然synchronized 鎖 等價於  Lock 鎖。 可是他們其實仍是有所區別的。

lock 鎖並非鎖當前的 this 對象的。

package com.my.com.my.sysnc;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 同步鎖測試
 */
public class TestSynchronized implements  Runnable{

    static  TestSynchronized instance=new TestSynchronized();

    Lock lock=new ReentrantLock();



    @Override
    public void run() {


      test1();
        test2();




    }

    public    synchronized void test1(){
        System.out.println("synchronized對象鎖代碼塊"+Thread.currentThread().getName());

        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("synchronized對象鎖代碼塊結束"+Thread.currentThread().getName());
    }

    public   void test2(){

        lock.lock();
        try {
            System.out.println("lock對象鎖代碼塊"+Thread.currentThread().getName());

            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }finally {
            //不釋放鎖
          //  lock.unlock();
        }



        System.out.println(Thread.currentThread().getName()+"結束線程lock");
    }


    public static void main(String[] args) {

        Thread thread1=new Thread(instance);
        Thread thread2=new Thread(instance);
        thread1.start();
        thread2.start();

    }

}


線程0 在lock 執行後,並無釋放lock 鎖。
因此線程1 會在 lock 處等待。 由於 lock 不是 鎖當前this 對象, 因此 線程1 能夠執行 synchronized 鎖方法
結果:


synchronized對象鎖代碼塊Thread-0
synchronized對象鎖代碼塊結束Thread-0
lock對象鎖代碼塊Thread-0
synchronized對象鎖代碼塊Thread-1
Thread-0結束線程lock
synchronized對象鎖代碼塊結束Thread-1

 

 

 

 

 

 

 

注意點

1, 鎖對象不能爲空,做用域不宜過大,避免死鎖

2, 如何選擇 Lock 和 synchronized 關鍵字

21, 能使用 synchronized 優先使用,可以使代碼更簡化

22.   要使用 LOCK 的獨有特性才考慮 lock  

23. 總之就是 避免出錯,追求穩定的

 

synchronized非公平鎖,ReentrantLock能夠設置是否公平鎖

參考 https://www.cnblogs.com/twoheads/p/10150063.html

優勢 缺點 適用場景
偏向鎖 加鎖和解鎖不須要額外的消耗,與執行非同步方法僅存在納秒級的差距 若是線程間存在競爭,會帶來額外的鎖撤銷的消耗 適用於只有一個線程訪問同步塊的狀況
輕量級鎖 競爭的線程不會堵塞,提升了程序的響應速度 始終得不到鎖的線程,使用自旋會消耗CPU 追求響應時間,同步塊執行速度很是塊,只有兩個線程競爭鎖
重量級鎖 線程競爭不使用自旋,不會消耗CPU 線程堵塞,響應時間緩慢 追求吞吐量,同步塊執行速度比較慢,競爭鎖的線程大於2個

 

 

以上來自慕課網

相關文章
相關標籤/搜索