多線程系列之自己實現一個 lock 鎖


我們面試中經常會被問到多線程相關知識,這一塊內容往淺了說大家都會,但是一問到底層實現原理,我們往往就一臉懵逼。

這段時間准備好好學習多線程,接下來會寫一系列關於多線程的知識。

我們首先要了解線程,百度百科這么介紹:線程(thread)是操作系統能夠進行運算調度的最小單位。它被包含在進程之中,是進程中的實際運作單位。一條線程指的是進程中一個單一順序的控制流,一個進程中可以並發多個線程每條線程並行執行不同的任務

這句話很好理解。一般我們不創建 thread 類的話,程序都是單線程在執行。

線程是有多種狀態的,Thread 類里有個枚舉變量 State 列舉了線程的幾種狀態

下面這張圖說明了線程狀態之間的關系。

 

程序里有多個線程時,會出現多個線程對同一個對象操作的情況,為了保證同一時間只能有一個線程操作該對象,就引入了鎖機制。

鎖有分布式鎖,java鎖 ,java 鎖有 synchronized 關鍵字,Lock 鎖。今天我們主要講 java 中的 Lock 鎖。

常用的可重入鎖(ReentrantLock)就是實現了 Lock 接口,這次我們就嘗試自己制作一把鎖,當然我們要實現 Lock 接口。

首先來分析如何實現它?

1.需要一個線程的等待集合,所以我們要定義一個list,考慮到先進先出機制,我們用 LinkedBlockingQueue 來存放線程集合

2.需要一個鎖標記,來記錄當前持有鎖的進程,考慮到它的原子性,我們用 AtomicReference 類來存放

3.線程執行完,會釋放鎖,此時要通知其他線程爭搶鎖,這里又涉及到線程通信。

 

下圖也是我的分析過程,我們接下來就用代碼來實現。

 

實現之前,我們先了解一些線程通信的知識,我們最常用的方法是 wait/notify,但是 wait/notify 有一個很明顯的缺點,正常順序是 先 wait,再 notify,如果程序先 notify,再 wait,就會進入死鎖狀態,所以它是有順序限制的,但是 park/unpark 方法就不會有這些問題,所以我們用 park/unpark 實現線程通信。

下面是實現代碼,注釋里寫的很清楚了。

public class MyLock implements Lock {

    //判斷是否有人拿到鎖
    AtomicReference<Thread> owner = new AtomicReference<>();
    //等待線程隊列
    LinkedBlockingQueue<Thread> waiters = new LinkedBlockingQueue<>();

    @Override
    public boolean tryLock() {
        //如果 owner 為空,就把當前線程賦給 owner
        return owner.compareAndSet(null,Thread.currentThread());
    }

    @Override
    public void lock() {
        boolean park = false;
        while(!tryLock()){
            if(!park){
                //加入等待集合
                waiters.offer(Thread.currentThread());
                park = true;
            }else{
                LockSupport.park();
            }
        }
        waiters.remove(Thread.currentThread());

    }

    @Override
    public void unlock() {
        //釋放鎖,要通知等待者
        if(owner.compareAndSet(Thread.currentThread(),null)){
            //遍歷等待者 ,通知繼續執行
            Thread next = null;
            while ((next = waiters.peek() )!= null){
                LockSupport.unpark(next);
            }

        }
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {
    }
    
    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return false;
    }
    
    @Override
    public Condition newCondition() {
        return null;
    }
}

 Lock 接口里,還有一些方法沒有實現,當然這都不重要,主要是為了實現 lock 和 unlock 方法。

研究多線程,要多看看 java.util.concurrent 包,把這個包研究透了,多線程也就學的差不多了。畢竟面試中多線程用的還是挺多的。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM