帶你理解Lock鎖原理


同樣是鎖,先說說synchronized和lock的區別:

  1. synchronized是java關鍵字,是用c++實現的;而lock是用java類,用java可以實現
  2. synchronized可以鎖住代碼塊,對象和類,但是線程從開始獲取鎖之后開發者不能進行控制和了解;lock則用起來非常靈活,提供了許多api可以讓開發者去控制加鎖和釋放鎖等等。

寫個Demo

static Lock lock = new ReentrantLock();
public
static void main(String[] args) throws InterruptedException { lock.lock();//其他沒拿到鎖的卡住不動 Thread thread = new Thread(new Runnable() { @Override public void run() { System.out.println("start to get lock Interruptibly"); lock.unlock(); //看看會發生什么,注釋掉再看看 lock.lock(); System.out.println("拿到鎖"); lock.unlock(); System.out.println("釋放鎖"); } }); thread.start(); Thread.sleep(3000); lock.unlock(); }

我們自己來手寫一下lock接口的tryLock()、lock()和unLock()方法,實現我們自己的myLock。

public class MyLock implements Lock {
    //多並發調用  0-未占用 大於0-占用
    AtomicInteger state = new AtomicInteger();

    Thread ownerThread = new Thread();

    //等待鎖的隊列
    LinkedBlockingQueue<Thread> waiters = new LinkedBlockingQueue();

    @Override
    public void lock() {
        if (!tryLock()) {  //先搶鎖,所以是非公平鎖
            //沒拿到鎖,放到隊列中去進行排隊
            waiters.add(Thread.currentThread());
            //等待被喚醒
            for (; ; ) {
                if (tryLock()) {  //非公平鎖情況下,喚醒過來繼續獲取鎖
                    waiters.poll(); //獲取鎖成功把自己從隊列中取出來
                    return;
                } else    //獲取鎖失敗
                    LockSupport.park();  //線程阻塞
            }
        }
    }


    @Override
    public boolean tryLock() {
        if (state.get() == 0) { //如果鎖沒被占用
            if (state.compareAndSet(0, 1)) {  //如果成功拿到鎖
                ownerThread = Thread.currentThread();   //占用鎖線程改為當前線程
                return true;
            }
        }
        return false;
    }

    @Override
    public void unlock() {

        if (ownerThread != Thread.currentThread())  //占用鎖線程不是當前線程無法釋放鎖
            throw new RuntimeException("非法調用,當前鎖不屬於你");

        if (state.decrementAndGet() == 0)  //如果成功釋放鎖
            ownerThread = null;  //占用鎖線程置空
        //通知其他線程
//        Thread thread = null;
//
//        while ((thread = waiters.peek()) != null)
//            LockSupport.unpark(thread);
        Thread thread = waiters.peek(); //獲取隊列頭部線程,線程還留在隊列中
        if (thread != null) {
            LockSupport.unpark(thread); //取消阻塞
        }
    }



    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return false;
    }


    @Override
    public Condition newCondition() {
        return null;
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {

    }
}

幾個注意點:

  • 鎖的占用狀態state是AtomicInteger類型,底層原理是CAS,這是為了保證在多並發情況下線程安全問題;
  • 當線程1釋放鎖成功時,獲取隊列頭部線程但並不取出,因為非公平鎖模式下,隊列頭部線程不一定能獲取到鎖;
  • LockSupport的park()和unPark()方法是native方法,可以阻塞,喚醒線程;

Lock默認是非公平鎖,上面實現的也是非公平鎖,小伙伴們可以試一試。

公平鎖和非公平鎖區別:

先等待先獲取鎖是公平鎖;先等待也不一定先獲取鎖,可能被突然到來的線程獲取到是非公平鎖;

公平鎖的實現:

  @Override
    public void lock() {
       checkQueue();//線程來的時候先不獲取鎖,而是先檢查隊列中有沒有等待的線程,如果有,直接放入隊列,如果沒有,再去獲取鎖
        if (!tryLock()) {  //先搶鎖,所以是非公平鎖
            //沒拿到鎖,放到隊列中去進行排隊
            waiters.add(Thread.currentThread());
            //等待被喚醒
            for (; ; ) {
                if (tryLock()) {  //非公平鎖情況下,喚醒過來繼續獲取鎖
                    waiters.poll(); //獲取鎖成功把自己從隊列中取出來
                    return;
                } else    //獲取鎖失敗
                    LockSupport.park();  //線程阻塞
            }
        }
    }

看完的小伙伴可以去看JDK提供的Lock源碼啦。。

 


免責聲明!

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



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