亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java中鎖的分類及如何實現

發布時間:2022-02-22 17:24:57 來源:億速云 閱讀:136 作者:iii 欄目:開發技術

這篇文章主要介紹了Java中鎖的分類及如何實現的相關知識,內容詳細易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇Java中鎖的分類及如何實現文章都會有所收獲,下面我們一起來看看吧。

    Lock和synchronized

    • 鎖是一種工具,用于控制對共享資源的訪問

    • Lock和synchronized,這兩個是最創建的鎖,他們都可以達到線程安全的目的,但是使用和功能上有較大不同

    • Lock不是完全替代synchronized的,而是當使用synchronized不合適或不足以滿足要求的時候,提供高級功能 

    • Lock 最常見的是ReentrantLock實現

    為啥需要Lock

    1. syn效率低:鎖的釋放情況少,試圖獲得鎖時不能設定超時,不能中斷一個正在試圖獲得鎖的線程

    2. 不夠靈活,加鎖和釋放的時機單一,每個鎖僅有一個單一的條件(某個對象),可能是不夠的

    3. 無法知道是否成功獲取到鎖

    主要方法

    Lock();     

    最普通的獲取鎖,最佳實踐是finally中釋放鎖,保證發生異常的時候鎖一定被釋放

        /**
         * 描述:Lock不會像syn一樣,異常的時候自動釋放鎖
         *      所以最佳實踐是finally中釋放鎖,保證發生異常的時候鎖一定被釋放
         */
        private static Lock lock = new ReentrantLock();
     
        public static void main(String[] args) {
            lock.lock();
            try {
                //獲取本鎖保護的資源
                System.out.println(Thread.currentThread().getName() + "開始執行任務");
            } finally {
                lock.unlock();
            }
        }

    tryLock(long time,TimeUnit unit);超時就放棄

    用來獲取鎖,如果當前鎖沒有被其它線程占用,則獲取成功,則返回true,否則返回false,代表獲取鎖失敗

    /**
         * 描述:用TryLock避免死鎖
         */
        static class TryLockDeadlock implements Runnable {
     
            int flag = 1;
     
            static Lock lock1 = new ReentrantLock();
            static Lock lock2 = new ReentrantLock();
     
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    if (flag == 1) {
                        try {
                            if (lock1.tryLock(800, TimeUnit.MILLISECONDS)) {
                                try {
                                    System.out.println("線程1獲取到了鎖1");
                                    Thread.sleep(new Random().nextInt(1000));
                                    if (lock2.tryLock(800,TimeUnit.MILLISECONDS)){
                                        try {
                                            System.out.println("線程1獲取到了鎖2");
                                            System.out.println("線程1成功獲取到了2把鎖");
                                            break;
                                        }finally {
                                            lock2.unlock();
                                        }
                                    }else{
                                        System.out.println("線程1獲取鎖2失敗,已重試");
                                    }
                                } finally {
                                    lock1.unlock();
                                    Thread.sleep(new Random().nextInt(1000));
                                }
                            } else {
                                System.out.println("線程1獲取鎖1失敗,已重試");
                            }
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
     
                    if (flag == 0) {
                        try {
                            if (lock2.tryLock(3000, TimeUnit.MILLISECONDS)) {
                                try {
                                    System.out.println("線程2獲取到了鎖2");
                                    Thread.sleep(new Random().nextInt(1000));
                                    if (lock1.tryLock(800,TimeUnit.MILLISECONDS)){
                                        try {
                                            System.out.println("線程2獲取到了鎖1");
                                            System.out.println("線程2成功獲取到了2把鎖");
                                            break;
                                        }finally {
                                            lock1.unlock();
                                        }
                                    }else{
                                        System.out.println("線程2獲取鎖1失敗,已重試");
                                    }
                                } finally {
                                    lock2.unlock();
                                    Thread.sleep(new Random().nextInt(1000));
                                }
                            } else {
                                System.out.println("線程2獲取鎖2失敗,已經重試");
                            }
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
     
            public static void main(String[] args) {
                TryLockDeadlock r1 = new TryLockDeadlock();
                TryLockDeadlock r2 = new TryLockDeadlock();
                r1.flag = 1;
                r2.flag = 0;
                new Thread(r1).start();
                new Thread(r2).start();
            }
        }
     
    執行結果:
    線程1獲取到了鎖1
    線程2獲取到了鎖2
    線程1獲取鎖2失敗,已重試
    線程2獲取到了鎖1
    線程2成功獲取到了2把鎖
    線程1獲取到了鎖1
    線程1獲取到了鎖2
    線程1成功獲取到了2把鎖

    lockInterruptibly(); 中斷

    相當于tryLock(long time,TimeUnit unit) 把超時時間設置為無限,在等待鎖的過程中,線程可以被中斷

    /**
         * 描述:獲取鎖的過程中,中斷了
         */
        static class LockInterruptibly implements Runnable {
     
            private Lock lock = new ReentrantLock();
     
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "嘗試獲取鎖");
                try {
                    lock.lockInterruptibly();
                    try {
                        System.out.println(Thread.currentThread().getName() + "獲取到了鎖");
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        System.out.println(Thread.currentThread().getName() + "睡眠中被中斷了");
                    } finally {
                        lock.unlock();
                        System.out.println(Thread.currentThread().getName() + "釋放了鎖");
                    }
                } catch (InterruptedException e) {
                    System.out.println(Thread.currentThread().getName() + "等鎖期間被中斷了");
                }
            }
     
            public static void main(String[] args) {
                LockInterruptibly lockInterruptibly = new LockInterruptibly();
                Thread thread0 = new Thread(lockInterruptibly);
                Thread thread1 = new Thread(lockInterruptibly);
                thread0.start();
                thread1.start();
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                thread0.interrupt();
            }
        }
     
    執行結果:
    Thread-0嘗試獲取鎖
    Thread-1嘗試獲取鎖
    Thread-0獲取到了鎖
    Thread-0睡眠中被中斷了
    Thread-0釋放了鎖
    Thread-1獲取到了鎖
    Thread-1釋放了鎖

    Java鎖分類:

    Java中鎖的分類及如何實現

    樂觀鎖和悲觀鎖:

    樂觀鎖:

    比較樂觀,認為自己在處理操作的時候,不會有其它線程來干擾,所以并不會鎖住操作對象

    • 在更新的時候,去對比我修改期間的數據有沒有被改變過,如沒有,就正常的修改數據

    • 如果數據和我一開始拿到的不一樣了,說明其他人在這段時間內改過,會選擇放棄,報錯,重試等策略

    • 樂觀鎖的實現一般都是利用CAS算法來實現的

    劣勢:

    可能造成ABA問題,就是不知道是不是修改過

    使用場景:

    適合并發寫入少的情況,大部分是讀取的場景,不加鎖的能讓讀取的性能大幅提高

    悲觀鎖:

    比較悲觀,認為如果我不鎖住這個資源,別人就會來爭搶,就會造成數據結果錯誤,所以它會鎖住操作對象,Java中悲觀鎖的實現就是syn和Lock相關類

    劣勢:

    • 阻塞和喚醒帶來的性能劣勢

    • 如果持有鎖的線程被永久阻塞,比如遇到了無限循環,死鎖等活躍性問題,那么等待該線程釋放鎖的那幾個線程,永遠也得不到執行

    • 優先級反轉,優先級低的線程拿到鎖不釋放或釋放的比較慢,就會造成這個問題

    使用場景:

    適合并發寫入多的情況,適用于臨界區持鎖時間比較長的情況:

    1. 臨界區有IO操作

    2. 臨界區代碼復雜或者循環量大

    3. 臨界區競爭非常激烈

    可重入鎖:

    可重入就是說某個線程已經獲得某個鎖,可以再次獲取鎖而不會出現死鎖

    ReentrantLock 和 synchronized 都是可重入鎖

    // 遞歸調用演示可重入鎖
        static class RecursionDemo{
     
            public static ReentrantLock lock = new ReentrantLock();
     
            private static void accessResource(){
                lock.lock();
                try {
                    System.out.println("已經對資源處理了");
                    if (lock.getHoldCount() < 5){
                        System.out.println("已經處理了"+lock.getHoldCount()+"次");
                        accessResource();
                    }
                }finally {
                    lock.unlock();
                }
            }
     
            public static void main(String[] args) {
                new RecursionDemo().accessResource();
            }
        }
     
     
    執行結果:
    已經對資源處理了
    已經處理了1次
    已經對資源處理了
    已經處理了2次
    已經對資源處理了
    已經處理了3次
    已經對資源處理了
    已經處理了4次
    已經對資源處理了

    ReentrantLock的其它方法

    • isHeldByCurrentThread 可以看出鎖是否被當前線程持有

    • getQueueLength()可以返回當前正在等待這把鎖的隊列有多長,一般這兩個方法是開發和調試時候使用,上線后用到的不多


    公平鎖和非公平鎖

    • 公平指的是按照線程請求的順序,來分配鎖;

    • 非公平指的是,不完全按照請求的順序,在一定情況下,可以插隊

    • 非公平鎖可以避免喚醒帶來的空檔期

    /**
     * 描述:演示公平鎖和非公平鎖
     */
    class FairLock{
     
        public static void main(String[] args) {
            PrintQueue printQueue = new PrintQueue();
            Thread[] thread = new Thread[10];
            for (int i = 0; i < 10; i++) {
                thread[i] = new Thread(new Job(printQueue));
            }
     
            for (int i = 0; i < 5; i++) {
                thread[i].start();
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
     
    }
     
    class Job implements Runnable{
     
        PrintQueue printQueue;
     
        public Job(PrintQueue printQueue) {
            this.printQueue = printQueue;
        }
     
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+"開始打印");
            printQueue.printJob(new Object());
            System.out.println(Thread.currentThread().getName()+"打印完成");
        }
    }
     
    class PrintQueue{    
        // true 公平,false是非公平
        private  Lock queueLock = new ReentrantLock(true);
        public void printJob(Object document){
            queueLock.lock();
            try {
                int duration = new Random().nextInt(10)+1;
                System.out.println(Thread.currentThread().getName()+"正在打印,需要"+duration+"秒");
                Thread.sleep(duration * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                queueLock.unlock();
            }
     
            queueLock.lock();
            try {
                int duration = new Random().nextInt(10)+1;
                System.out.println(Thread.currentThread().getName()+"正在打印,需要"+duration+"秒");
                Thread.sleep(duration * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                queueLock.unlock();
            }
     
        }
    }
     
    執行結果:
    Thread-0開始打印
    Thread-0正在打印,需要10秒
    Thread-1開始打印
    Thread-2開始打印
    Thread-3開始打印
    Thread-4開始打印
    Thread-1正在打印,需要2秒
    Thread-2正在打印,需要2秒
    Thread-3正在打印,需要2秒
    Thread-4正在打印,需要4秒
    Thread-0正在打印,需要2秒
    Thread-0打印完成
    Thread-1正在打印,需要7秒
    Thread-1打印完成
    Thread-2正在打印,需要8秒
    Thread-2打印完成
    Thread-3正在打印,需要3秒
    Thread-3打印完成
    Thread-4正在打印,需要8秒
    Thread-4打印完成
     
    true改為false演示非公平鎖:
    Lock queueLock = new ReentrantLock(false);
    執行結果:
    Thread-0正在打印,需要7秒
    Thread-1開始打印
    Thread-2開始打印
    Thread-3開始打印
    Thread-4開始打印
    Thread-0正在打印,需要9秒
    Thread-0打印完成
    Thread-1正在打印,需要3秒
    Thread-1正在打印,需要2秒
    Thread-1打印完成
    Thread-2正在打印,需要4秒
    Thread-2正在打印,需要7秒
    Thread-2打印完成
    Thread-3正在打印,需要10秒
    Thread-3正在打印,需要2秒
    Thread-3打印完成
    Thread-4正在打印,需要7秒
    Thread-4正在打印,需要8秒
    Thread-4打印完成

    共享鎖和排它鎖:

    • 排它鎖,又稱為獨占鎖,獨享鎖

    • 共享鎖,又稱為讀鎖,獲得共享鎖之后,可以查看但無法修改和刪除數據,其他線程此時也可以獲取到共享鎖,也可以查看但無法修改和刪除數據

    • 共享鎖和排它鎖的典型是讀寫鎖 ReentrantReadWriteLock,其中讀鎖是共享鎖,寫鎖是獨享鎖

    讀寫鎖的作用:

    • 在沒有讀寫鎖之前,我們假設使用ReentrantLock,那么雖然我們保證了線程安全,但是也浪費了一定的資源:


      /**
       * 描述:演示可以多個一起讀,只能一個寫
       */
      class CinemaReadWrite{
          private static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
          private static ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
          private static ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
       
          private static void read(){
              readLock.lock();
              try {
                  System.out.println(Thread.currentThread().getName() + "得到了讀鎖,正在讀取");
                  Thread.sleep(1000);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              } finally {
                  System.out.println(Thread.currentThread().getName() + "釋放了讀鎖");
                  readLock.unlock();
              }
          }
       
          private static void write(){
              writeLock.lock();
              try {
                  System.out.println(Thread.currentThread().getName() + "得到了寫鎖,正在寫入");
                  Thread.sleep(1000);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              } finally {
                  System.out.println(Thread.currentThread().getName() + "釋放了寫鎖");
                  writeLock.unlock();
              }
          }
       
          public static void main(String[] args) {
              new Thread(()-> read(),"Thrad1").start();
              new Thread(()-> read(),"Thrad2").start();
              new Thread(()-> write(),"Thrad3").start();
              new Thread(()-> write(),"Thrad4").start();
          }
      }
       
      執行結果:
      Thrad1得到了讀鎖,正在讀取
      Thrad2得到了讀鎖,正在讀取
      Thrad2釋放了讀鎖
      Thrad1釋放了讀鎖
      Thrad3得到了寫鎖,正在寫入
      Thrad3釋放了寫鎖
      Thrad4得到了寫鎖,正在寫入

      Thrad4釋放了寫鎖多個讀操作同時進行,并沒有線程安全問題

    • 在讀的地方使用讀鎖,在寫的地方使用寫鎖,靈活控制,如果沒有寫鎖的情況下,讀是無阻塞的,提高了程序的執行效率

    讀寫鎖的規則:

    1. 多個線程值申請讀鎖,都可以申請到

    2. 要么一個或多個一起讀,要么一個寫,兩者不會同時申請到,只能存在一個寫鎖

    讀鎖和寫鎖的交互方式:

    讀鎖插隊策略:

    • 公平鎖:不允許插隊

    • 非公平鎖:寫鎖可以隨時插隊,讀鎖僅在等待隊列頭節點不是想獲取寫鎖線程的時候可以插隊

    自旋鎖和阻塞鎖

    • 讓當前線程進行自旋,如果自旋完成后前面鎖定同步資源的線程已經釋放了鎖,那么當前線程就可以不必阻塞而是直接獲取同步資源,從而避免切換線程的開銷。這就是自旋鎖。

    • 阻塞鎖和自旋鎖相反,阻塞鎖如果遇到沒拿到鎖的情況,會直接把線程阻塞,知道被喚醒

    自旋缺點:

    • 如果鎖被占用的時間很長,那么自旋的線程只會白浪費處理器資源

    • 在自旋的過程中,一直消耗cpu,所以雖然自旋鎖的起始開銷低于悲觀鎖,但是隨著自旋的時間增長,開銷也是線性增長的

    原理:

    • 在Java1.5版本及以上的并發框架java.util.concurrent 的atmoic包下的類基本都是自旋鎖的實現

    • AtomicInteger的實現:自旋鎖的實現原理是CAS,AtomicInteger中調用unsafe 進行自增操作的源碼中的do-while循環就是一個自旋操作,如果修改過程中遇到其他線程競爭導致沒修改成功,就在while里死循環直至修改成功

    /**
     * 描述:自旋鎖演示
     */
    class SpinLock{
        private AtomicReference<Thread> sign = new AtomicReference<>();
     
        public void lock(){
            Thread currentThread = Thread.currentThread();
            while (!sign.compareAndSet(null,currentThread)){
                System.out.println("自旋獲取失敗,再次嘗試");
            }
        }
     
        public void unLock(){
            Thread currentThread = Thread.currentThread();
            sign.compareAndSet(currentThread,null);
        }
     
        public static void main(String[] args) {
            SpinLock spinLock = new SpinLock();
            Runnable runnable = new Runnable(){
                @Override
                public void run(){
                    System.out.println(Thread.currentThread().getName()+"開始嘗試自旋鎖");
                    spinLock.lock();
                    System.out.println(Thread.currentThread().getName()+"獲取到了自旋鎖");
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }finally {
                        spinLock.unLock();
                        System.out.println(Thread.currentThread().getName()+"釋放了自旋鎖");
                    }
                }
            };
     
            Thread thread1 = new Thread(runnable);
            Thread thread2 = new Thread(runnable);
            thread1.start();
            thread2.start();
        }
    }
     
     
    執行結果:
    Thread-0開始嘗試自旋鎖
    Thread-0獲取到了自旋鎖
    Thread-1開始嘗試自旋鎖
    自旋獲取失敗,再次嘗試
    自旋獲取失敗,再次嘗試
    自旋獲取失敗,再次嘗試
    自旋獲取失敗,再次嘗試
    自旋獲取失敗,再次嘗試
    自旋獲取失敗,再次嘗試
    自旋獲取失敗,再次嘗試
    自旋獲取失敗,再次嘗試
    自旋獲取失敗,再次嘗試
    自旋獲取失敗,再次嘗試
    自旋獲取失敗,再次嘗試
    Thread-0釋放了自旋鎖
    Thread-1獲取到了自旋鎖
    Thread-1釋放了自旋鎖

    使用場景:

    • 自旋鎖一般用于多核服務器,在并發度不是特別高的情況下,比阻塞鎖的效率要高

    • 另外,自旋鎖適用于臨界區比較短小的情況,否則如果臨界區很大(線程一旦拿到鎖,很久之后才會釋放),那也是不合適的

    關于“Java中鎖的分類及如何實現”這篇文章的內容就介紹到這里,感謝各位的閱讀!相信大家對“Java中鎖的分類及如何實現”知識都有一定的了解,大家如果還想學習更多知識,歡迎關注億速云行業資訊頻道。

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    九江市| 政和县| 陆河县| 江孜县| 武安市| 织金县| 普格县| 张家口市| 乌什县| 汝阳县| 德昌县| 运城市| 清远市| 洞口县| 佛山市| 峨眉山市| 双柏县| 白城市| 辰溪县| 嘉黎县| 石门县| 枣庄市| 孙吴县| 兴化市| 手机| 吉木萨尔县| 仪征市| 神木县| 麟游县| 石狮市| 河北区| 博兴县| 平遥县| 宜昌市| 深水埗区| 武隆县| 安平县| 阳新县| 台南县| 莱阳市| 罗定市|