Java多線程與併發_Java鎖

Java多線程與併發_Java鎖

累嗎?累就對了,說明你還活着

一、公平鎖與非公平鎖

公平鎖:是指多個線程按照申請鎖的順序來獲取鎖,類似排隊打飯,先來後到。

非公平鎖:是指多個線程獲取鎖的順序並不是按照申請鎖的順序,有可能後申請的線程比先申請的線程優先獲取鎖。在高併發的情況下,有可能會造成優先級反轉或者飢餓現象

併發包中ReentrantLock的創建可以指定構造函數boolean類型來得到公平鎖或非公平鎖,默認是非公平鎖

    /**
     * Creates an instance of {@code ReentrantLock}.
     * This is equivalent to using {@code ReentrantLock(false)}.
     */
    public ReentrantLock() {
        sync = new NonfairSync();
    }
    
    /**
     * Creates an instance of {@code ReentrantLock} with the
     * given fairness policy.
     *
     * @param fair {@code true} if this lock should use a fair ordering policy
     */
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

兩者區別

  • 公平鎖:就是很公平,在併發環境中,每個線程在獲取鎖時會先查看此鎖維護的等待隊列,如果爲空,或者當前線程是等待隊列的第一個,就佔優鎖,否則就會加入到等待隊列中,以後會按照FIFO的規則從隊列中取到自己

  • 非公平鎖:非公平鎖不比較粗魯,上來就直接嘗試佔有鎖,如果嘗試失敗,就再採用類似公平鎖那種方式

二、可重入鎖(遞歸鎖)

指的是同一線程外層函數獲得鎖之後,內層遞歸函數仍然能獲取該鎖的代碼,在同一個線程在外層方法獲取鎖的時候,在進入內層方法會自動獲取鎖

也就是說,線程可以進入任何一個它已經擁有的鎖所同步着的代碼塊

class Phone implements Runnable{
    public synchronized void sendSMS() {
        System.out.println(Thread.currentThread().getName() + "\t invoked sendSMS()");
        sendEmail();
    }

    public synchronized void sendEmail() {
        System.out.println(Thread.currentThread().getName() + "\t ########invoked sendEmail()");
    }

    Lock lock = new ReentrantLock();
    @Override
    public void run() {
        get();
    }

    public void get() {
        lock.lock();
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + "\t invoked get()");
            set();

        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
            lock.unlock();
        }
    }

    public void set() {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + "\t invoked set()");

        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}
    Phone phone = new Phone();

        new Thread(() -> {
            try {
                phone.sendSMS();
            }catch (Exception e) {
                e.printStackTrace();
            }
        },"t1").start();

        new Thread(() -> {
            try {
                phone.sendSMS();
            }catch (Exception e) {
                e.printStackTrace();
            }
        },"t2").start();

sychoronized可重入鎖結果:

t1	 invoked sendSMS()             t1線程在外層方法獲取鎖的時候
t1	 ########invoked sendEmail()   t1在進入內層方法會自動獲取鎖
t2	 invoked sendSMS()
t2	 ########invoked sendEmail()

Process finished with exit code 0
    Thread t3 = new Thread(phone,"t3");
        Thread t4 = new Thread(phone,"t4");
        t3.start();
        t4.start();

ReentrantLock可重入鎖結果:

t3	 invoked get()
t3	 invoked set()
t4	 invoked get()
t4	 invoked set()

Process finished with exit code 0

三、自旋鎖

指嘗試獲取鎖的線程不會立即阻塞,而是採用循環的方式去嘗試獲取鎖,這樣的好處是減少線程上下文的消耗,缺點是循環會消耗CPU

實現一個自旋鎖

自旋鎖好處:循環比較獲取直到成功爲止,沒有類似wait的阻塞
通過CAS操作完成自旋鎖,A線程先進來調用myLock方法自己持有鎖5秒,B隨後進來後發現
當前有線程持有鎖,不是null,所以只能通過自旋等待,直到A釋放鎖後B隨後搶到
public class SpinLockDemo {

    //原子引用線程
    AtomicReference<Thread> atomicReference = new AtomicReference<>();

    public void myLock() {
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName() + "\t come in");
        while(!atomicReference.compareAndSet(null,thread)) {

        }
    }

    public void myUnLock() {
        Thread thread = Thread.currentThread();
        atomicReference.compareAndSet(thread,null);
        System.out.println(Thread.currentThread().getName() + "\t invoked myUnLock()");
    }

    public static void main(String[] args) {
        SpinLockDemo spinLockDemo = new SpinLockDemo();
        new Thread(() -> {
            spinLockDemo.myLock();
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            spinLockDemo.myUnLock();
        },"AA").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            spinLockDemo.myLock();
            spinLockDemo.myUnLock();
        },"BB").start();

    }
}

結果

AA	 come in
BB	 come in
AA	 invoked myUnLock()
BB	 invoked myUnLock()

Process finished with exit code 0
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章