<input id="ohw05"></input>
  • <table id="ohw05"><menu id="ohw05"></menu></table>
  • <var id="ohw05"></var>
  • <code id="ohw05"><cite id="ohw05"></cite></code>
    <label id="ohw05"></label>
    <var id="ohw05"></var>
  • 用代碼說話:synchronized關鍵字和多線程訪問同步方法的7種情況

    synchronized關鍵字在多線程并發編程中一直是元老級角色的存在,是學習并發編程中必須面對的坎,也是走向Java高級開發的必經之路。

    一、synchronized性質

    synchronized是Java提供的內置鎖機制,有如下兩種特性:

    • 互斥性:即在同一時間最多只有一個線程能持有這種鎖。當線程1嘗試去獲取一個由線程2持有的鎖時,線程1必須等待或者阻塞,知道線程2釋放這個鎖。如果線程2永遠不釋放鎖,那么線程1將永遠等待下去。

    • 可重入性:即某個線程可以獲取一個已經由自己持有的鎖。

    二、synchronized用法

    Java中的每個對象都可以作為鎖。根據鎖對象的不同,synchronized的用法可以分為以下兩種:

    • 對象鎖:包括方法鎖(默認鎖對象為this當前實例對象)和同步代碼塊鎖(自己制定鎖對象)

    • 類鎖:指的是synchronized修飾靜態的方法或指定鎖為Class對象。

    三、多線程訪問同步方法的7種情況

    本部分針對面試中常考的7中情況進行代碼實戰和原理解釋。

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

    /**
    * 兩個線程同時訪問一個對象的同步方法
    */
    public class Demo1 implements Runnable {
    
        static Demo1 instance = new Demo1();
    
        @Override
        public void run() {
            fun();
        }
    
        public synchronized void fun() {
            System.out.println(Thread.currentThread().getName() + "開始運行");
            try {
                Thread.sleep(2000);
            } 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();
            while (thread1.isAlive() || thread2.isAlive()) {
    
            }
            System.out.println("finished");
        }
    }
    

    結果:兩個線程順序執行。

    兩個線程同時訪問一個對象的同步方法

    解釋:thread1和thread2共用一把鎖instance;同一時刻只能有一個線程獲取鎖;thread1先啟動,先獲得到鎖,先運行,此時thread2只能等待。當thread1釋放鎖之后,thread2獲取到鎖,進行執行。

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

    public class Demo2 implements Runnable{
    
        static Demo2 instance1 = new Demo2();
        static Demo2 instance2 = new Demo2();
    
        @Override
        public void run() {
            fun();
        }
    
        public synchronized void fun() {
            System.out.println(Thread.currentThread().getName() + "開始運行");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "運行結束");
        }
    
        public static void main(String[] args) {
            Thread thread1 = new Thread(instance1);
            Thread thread2 = new Thread(instance2);
            thread1.start();
            thread2.start();
            while (thread1.isAlive() || thread2.isAlive()) {
    
            }
            System.out.println("finished");
        }
    }
    

    結果: 兩個線程并行執行。

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

    解釋:thread1使用的鎖對象是instance1,thread2使用的鎖對象是instance2,兩個對象使用的鎖對象不是同一個,所以線程之間互不影響,是并行執行的。

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

    public class Demo3 implements Runnable{
    
        static Demo3 instance1 = new Demo3();
        static Demo3 instance2 = new Demo3();
    
        @Override
        public void run() {
            fun();
        }
    
        public static synchronized void fun() {
            System.out.println(Thread.currentThread().getName() + "開始運行");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "運行結束");
        }
    
        public static void main(String[] args) {
            Thread thread1 = new Thread(instance1);
            Thread thread2 = new Thread(instance2);
            thread1.start();
            thread2.start();
            while (thread1.isAlive() || thread2.isAlive()) {
    
            }
            System.out.println("finished");
        }
    }
    

    結果:兩個線程順序執行。

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

    解釋:雖然兩個線程使用了兩個不同的instance實例,但是只要方法是靜態的,對應的鎖對象是同一把鎖,需要先后獲取到鎖進行執行。

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

    public class Demo4 implements Runnable {
    
        static Demo4 instance = new Demo4();
    
        @Override
        public void run() {
            if (Thread.currentThread().getName().equals("Thread-0")){
                fun1();
            }else{
                fun2();
            }
        }
    
        public synchronized void fun1() {
            System.out.println(Thread.currentThread().getName() + "開始運行");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "fun1運行結束");
        }
    
        public void fun2() {
            System.out.println(Thread.currentThread().getName() + "fun2開始運行");
            try {
                Thread.sleep(2000);
            } 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();
            while (thread1.isAlive() || thread2.isAlive()) {
    
            }
            System.out.println("finished");
        }
    }
    

    結果:兩個線程并行執行。

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

    解釋:synchronize的關鍵字只對fun1起作用,不會對其他方法造成影響。也就是說同步方法不會對非同步方法造成影響,兩個方法并行執行。

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

    public class Demo5 implements Runnable {
    
        static Demo5 instance = new Demo5();
    
        @Override
        public void run() {
            if (Thread.currentThread().getName().equals("Thread-0")){
                fun1();
            }else{
                fun2();
            }
        }
    
        public synchronized void fun1() {
            System.out.println(Thread.currentThread().getName() + "開始運行");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "fun1運行結束");
        }
    
        public synchronized void fun2() {
            System.out.println(Thread.currentThread().getName() + "fun2開始運行");
            try {
                Thread.sleep(2000);
            } 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();
            while (thread1.isAlive() || thread2.isAlive()) {
    
            }
            System.out.println("finished");
        }
    }
    

    結果:順序執行。
     訪問同一個對象的不同的普通同步方法

    解釋:兩個方法共用了instance對象鎖,兩個方法無法同時運行,只能先后運行。

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

    public class Demo6 implements Runnable{
    
        static Demo6 instance = new Demo6();
    
        @Override
        public void run() {
            if (Thread.currentThread().getName().equals("Thread-0")){
                fun1();
            }else{
                fun2();
            }
        }
    
        public static synchronized void fun1() {
            System.out.println(Thread.currentThread().getName() + "開始運行");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "fun1運行結束");
        }
    
        public synchronized void fun2() {
            System.out.println(Thread.currentThread().getName() + "fun2開始運行");
            try {
                Thread.sleep(2000);
            } 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();
            while (thread1.isAlive() || thread2.isAlive()) {
    
            }
            System.out.println("finished");
        }
    }
    

    結果:兩個線程并行執行

    同時訪問靜態synchronized和非靜態的synchronized方法

    解釋:有static關鍵字,鎖的是類本身;沒有static關鍵字,鎖的是對象實例;鎖不是同一把鎖,兩個鎖之間是沒有沖突的;所以兩個線程可以并行執行。

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

    public class Demo7 implements Runnable{
    
        static Demo7 instance = new Demo7();
    
        @Override
        public void run() {
            if (Thread.currentThread().getName().equals("Thread-0")){
                fun1();
            }else{
                fun2();
            }
        }
    
        public synchronized void fun1() {
            System.out.println(Thread.currentThread().getName() + "開始運行");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            throw new RuntimeException();
            //System.out.println(Thread.currentThread().getName() + "fun1運行結束");
        }
    
        public synchronized void fun2() {
            System.out.println(Thread.currentThread().getName() + "fun2開始運行");
            try {
                Thread.sleep(2000);
            } 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();
            while (thread1.isAlive() || thread2.isAlive()) {
    
            }
            System.out.println("finished");
        }
    }
    

    結果:thread1運行時遇到異常,并未運行結束,thread2開始運行,并運行至結束。

    方法拋異常后,會釋放鎖

    解釋:方法拋出異常后,JVM自動釋放鎖。

    8. 上述7種情況總結

    3點核心思想:

    1. 一把鎖只能同時被一個線程獲取,沒有拿到鎖的線程必須等待。

    2. 每個實例都對應有自己的一把鎖,不同實例之間互不影響;例外:鎖對象是.class以及synchronized修飾的是static方法的時候,所有對象共用同一把鎖。

    3. 無論是方法正常運行完畢或者方法拋出異常,都會釋放鎖。

    四、synchronized和ReentrantLock比較

    雖然ReentrantLock是更加高級的鎖機制,但是synchronized依然存在著如下的優點:

    1. synchronized作為內置鎖為更多的開發人員所熟悉,代碼簡潔;

    2. synchronized較ReentrantLock更加安全,ReentrantLock如果忘記在finally中釋放鎖,雖然代碼表面上運行正常,但實際上已經留下了隱患

    3. synchronized在線程轉儲中能給出在哪些調用幀中獲得了哪些瑣,并能夠檢測和識別發生死鎖的線程。

    五、總結

    1. synchronized關鍵字是Java提供的一種互斥的、可重入的內置鎖機制。

    2. 其有兩種用法:對象鎖和類鎖。

    3. 雖然synchronized與高級鎖相比有著不夠靈活、效率低等不足,但也有自身的優勢:安全,依然是并發編程領域不得不學習的重要知識點。

    posted @ 2019-08-24 16:46  James_Shangguan  閱讀(1549)  評論(0編輯  收藏  舉報
    国产美女a做受大片观看