亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

詳解Java中的鎖Lock和synchronized

 更新時(shí)間:2021年06月11日 09:25:38   作者:CoderSheeper  
鎖是Java并發(fā)編程中最重要的同步機(jī)制,Java提供了種類豐富的鎖,每種鎖因其特性的不同,在適當(dāng)?shù)膱?chǎng)景下能夠展現(xiàn)出非常高的效率。本文將詳細(xì)介紹Lock和synchronized

一、Lock接口

1、Lock接口和synchronized內(nèi)置鎖

a)synchronized:Java提供的內(nèi)置鎖機(jī)制,Java中的每個(gè)對(duì)象都可以用作一個(gè)實(shí)現(xiàn)同步的鎖(內(nèi)置鎖或者監(jiān)視器Monitor),線程在進(jìn)入同步代碼塊之前需要或者這把鎖,在退出同步代碼塊會(huì)釋放鎖。而synchronized這種內(nèi)置鎖實(shí)際上是互斥的,即沒把鎖最多只能由一個(gè)線程持有。

b)Lock接口:Lock接口提供了與synchronized相似的同步功能,和synchronized(隱式的獲取和釋放鎖,主要體現(xiàn)在線程進(jìn)入同步代碼塊之前需要獲取鎖退出同步代碼塊需要釋放鎖)不同的是,Lock在使用的時(shí)候是顯示的獲取和釋放鎖。雖然Lock接口缺少了synchronized隱式獲取釋放鎖的便捷性,但是對(duì)于鎖的操作具有更強(qiáng)的可操作性、可控制性以及提供可中斷操作和超時(shí)獲取鎖等機(jī)制。

2、lock接口使用的一般形式

Lock lock = new ReentrantLock(); //這里可以是自己實(shí)現(xiàn)Lock接口的實(shí)現(xiàn)類,也可以是jdk提供的同步組件
lock.lock();//一般不將鎖的獲取放在try語句塊中,因?yàn)槿绻l(fā)生異常,在拋出異常的同時(shí),也會(huì)導(dǎo)致鎖的無故釋放
try {
}finally {
    lock.unlock(); //放在finally代碼塊中,保證鎖一定會(huì)被釋放
}

3、Lock接口的方法

public interface Lock {

    /**
     * 獲取鎖,調(diào)用該方法的線程會(huì)獲取鎖,當(dāng)獲取到鎖之后會(huì)從該方法但會(huì)
     */
    void lock();

    /**
     * 可響應(yīng)中斷。即在獲取鎖的過程中可以中斷當(dāng)前線程
     */
    void lockInterruptibly() throws InterruptedException;

    /**
     * 嘗試非阻塞的獲取鎖,調(diào)用該方法之后會(huì)立即返回,如果獲取到鎖就返回true否則返回false
     */
    boolean tryLock();

    /**
     * 超時(shí)的獲取鎖,下面的三種情況會(huì)返回
     * ①當(dāng)前線程在超時(shí)時(shí)間內(nèi)獲取到了鎖
     * ②當(dāng)前線程在超時(shí)時(shí)間內(nèi)被中斷
     * ③超時(shí)時(shí)間結(jié)束,返回false
     */
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

    /**
     * 釋放鎖
     */
    void unlock();

    /**
     * 獲取等待通知組件,該組件和當(dāng)前鎖綁定,當(dāng)前線程只有獲取到了鎖才能調(diào)用組件的wait方法,調(diào)用該方法之后會(huì)釋放鎖
     */
    Condition newCondition();
}

4、相比于synchronized,Lock接口所具備的其他特性

①嘗試非阻塞的獲取鎖tryLock():當(dāng)前線程嘗試獲取鎖,如果該時(shí)刻鎖沒有被其他線程獲取到,就能成功獲取并持有鎖

②能被中斷的獲取鎖lockInterruptibly():獲取到鎖的線程能夠響應(yīng)中斷,當(dāng)獲取到鎖的線程被中斷的時(shí)候,會(huì)拋出中斷異常同時(shí)釋放持有的鎖

③超時(shí)的獲取鎖tryLock(long time, TimeUnit unit):在指定的截止時(shí)間獲取鎖,如果沒有獲取到鎖返回false

二、重入鎖

1、重入鎖的概念

當(dāng)某個(gè)線程請(qǐng)求一個(gè)被其他線程所持有的鎖的時(shí)候,該線程會(huì)被阻塞(后面的讀寫鎖先不考慮在內(nèi)),但是像synchronized這樣的內(nèi)置鎖是可重入的,即一個(gè)線程試圖獲取一個(gè)已經(jīng)被該線程所持有的鎖,這個(gè)請(qǐng)求會(huì)成功。重入以為這鎖的操作粒度是線程級(jí)別而不是調(diào)用級(jí)別。我們下面說到的ReentrantLock也是可重入的,而除了支持鎖的重入之外,該同步組件也支持公平的和非公平的選擇。

2、ReentrantLock

a)ReentrantLock實(shí)現(xiàn)的可重入性

對(duì)于鎖的可重入性,需要解決的兩個(gè)問題就是:

①線程再次獲取鎖的識(shí)別問題(鎖需要識(shí)別當(dāng)前要獲取鎖的線程是否為當(dāng)前占有鎖的線程);

②鎖的釋放(同一個(gè)線程多次獲取同一把鎖,那么鎖的記錄也會(huì)不同。一般來說,當(dāng)同一個(gè)線程重復(fù)n次獲取鎖之后,只有在之后的釋放n次鎖之后,其他的線程才能去競(jìng)爭(zhēng)這把鎖)

③ReentrantLock的可重入測(cè)試

import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 
 public class TestCR {
     Lock lock = new ReentrantLock();
     
     void m1(){
         try{
             lock.lock(); // 加鎖
             for(int i = 0; i < 4; i++){
                 TimeUnit.SECONDS.sleep(1);
                 System.out.println("m1() method " + i);
             }
             m2(); //在釋放鎖之前,調(diào)用m2方法
         }catch(InterruptedException e){
             e.printStackTrace();
         }finally{
             lock.unlock(); // 解鎖
         }
     }
     
     void m2(){
         lock.lock();
         System.out.println("m2() method");
         lock.unlock();
     }
     
     public static void main(String[] args) {
         final TestCR t = new TestCR();
         new Thread(new Runnable() {
             @Override
             public void run() {
                 t.m1();
             }
         }).start();
 
         new Thread(new Runnable() {
             @Override
             public void run() {
                 t.m2();
             }
         }).start();
     }
 }

b)下面分析ReentrantLock的部分源碼來學(xué)習(xí)這個(gè)同步組件(默認(rèn)的非公平鎖實(shí)現(xiàn))

①首先可以知道ReentrantLock實(shí)現(xiàn)Lock接口public class ReentrantLock implements Lock

abstract static class Sync extends AbstractQueuedSynchronizer {
    /**
     * 創(chuàng)建非公平鎖的方法
     */
    abstract void lock();

    /**
     * 執(zhí)行非公平的tryLock。 tryAcquire實(shí)現(xiàn)于
     * 子類,但兩者都需要tryf方法的非公平嘗試。
     */
    final boolean nonfairTryAcquire(int acquires) {
        final Thread current = Thread.currentThread();//獲取當(dāng)前線程
        int c = getState(); //獲取當(dāng)前同步狀態(tài)的值
        if (c == 0) { //如果當(dāng)前的同步狀態(tài)還沒有被任何線程獲取
            if (compareAndSetState(0, acquires)) { //就更新同步狀態(tài)的值,因?yàn)橐呀?jīng)有線程獲取到同步裝填
                setExclusiveOwnerThread(current);//設(shè)置同步狀態(tài)的線程擁有者為當(dāng)前獲取的線程
                return true;
            }
        }
        else if (current == getExclusiveOwnerThread()) {//增加再次獲取同步狀態(tài)的處理邏輯
            int nextc = c + acquires; //如果再次嘗試獲取同步狀態(tài)的線程就是當(dāng)前已經(jīng)占有同步狀態(tài)的線程,那么就更新同步狀態(tài)的值(進(jìn)行增加操作)
            if (nextc < 0) // 對(duì)同步狀態(tài)的值進(jìn)行非法判斷
                throw new Error("Maximum lock count exceeded");
            setState(nextc); //更新state的值
            return true;
        }
        return false;
    }

    /**
     * 釋放同步狀態(tài)的處理邏輯
     */
    protected final boolean tryRelease(int releases) {
        int c = getState() - releases; //對(duì)同一線程而言,就是減去相應(yīng)的獲取次數(shù)
        if (Thread.currentThread() != getExclusiveOwnerThread())
            throw new IllegalMonitorStateException();
        boolean free = false; //返回值
        if (c == 0) { //只有該線程將獲取的次數(shù)全部釋放之后,才會(huì)返回true,并且將當(dāng)前同步狀態(tài)的持有者設(shè)置為null
            free = true;
            setExclusiveOwnerThread(null);
        }
        setState(c); //更新state
        return free;
    }

        /**
         * 判斷當(dāng)前同步狀態(tài)的持有者線程
         */
    protected final boolean isHeldExclusively() {
        return getExclusiveOwnerThread() == Thread.currentThread();
    }

    final ConditionObject newCondition() {
        return new ConditionObject();
    }

        /**
         * 返回當(dāng)前持有者線程
         */
    final Thread getOwner() {
        return getState() == 0 ? null : getExclusiveOwnerThread();
    }

        /**
         * 返回持有同步狀態(tài)的線程獲取次數(shù)
         */
    final int getHoldCount() {
        return isHeldExclusively() ? getState() : 0;
    }

        /**
         * 判斷當(dāng)前是否有線程獲取到同步狀態(tài)(根據(jù)state值進(jìn)行判斷)
         */
    final boolean isLocked() {
        return getState() != 0;
    }

    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        s.defaultReadObject();
        setState(0); // reset to unlocked state
    }
}

②通過上面的非公平鎖的實(shí)現(xiàn)源碼可以看到,ReentrantLock實(shí)現(xiàn)可重入的邏輯大概上是這樣的:

獲取邏輯:首先通過nonfairTryAcquire方法增加了對(duì)于同一線程再次獲取同步狀態(tài)的邏輯處理(通過判斷當(dāng)前線程是否為已經(jīng)同步狀態(tài)的持有者,來決定是否能夠再次獲取同步狀態(tài),如果當(dāng)前線程是已經(jīng)獲取到同步狀態(tài)的那個(gè)線程,那么就能夠獲取成功,并且同時(shí)以CAS的方式修改state的值)

釋放邏輯:對(duì)于成功獲取到同步狀態(tài)的線程,在釋放鎖的時(shí)候,通過tryRelease方法的實(shí)現(xiàn)可以看出,如果該鎖被線程獲取到了n次,那么前(n-1)次釋放的操作都會(huì)返回false,只有將同步狀態(tài)完全釋放才會(huì)返回true。最終獲取到同步狀態(tài)的線程在完全釋放掉之后,state值為0并且持有鎖的線程為null。

c)關(guān)于ReentrantLock的公平和非公平實(shí)現(xiàn)

①非公平鎖

公平和非公平是針對(duì)于獲取鎖而言的,對(duì)于公平鎖而言獲取鎖應(yīng)該遵循FIFO原則,上面我們通過源碼分析了非公平鎖的實(shí)現(xiàn)(對(duì)于非公平鎖而言,tryAcquire方法直接使用的是ReentrantLock靜態(tài)內(nèi)部類Sync的nofairTryAcquire方法)

//非公平鎖實(shí)現(xiàn)
static final class NonfairSync extends Sync {

    /**
     * 以CAS方式原子的更新state的值
     */
    final void lock() {
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
            acquire(1);
    }

    /**
     * 非公平鎖的實(shí)現(xiàn)是直接調(diào)用Sync的nonfairTryAcquire方法
     */
    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}

②公平鎖實(shí)現(xiàn)

公平鎖的實(shí)現(xiàn)和非公平實(shí)現(xiàn)的主要區(qū)別就是tryAcquire方法的實(shí)現(xiàn)

static final class FairSync extends Sync {

    final void lock() {
        acquire(1); //調(diào)用AQS的模板方法實(shí)現(xiàn)鎖的獲取
    }

    /**
     * 公平鎖的處理邏輯
     */
    protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread(); //獲取當(dāng)前線程
        int c = getState(); //獲取當(dāng)前同步狀態(tài)的值
        if (c == 0) { //當(dāng)前同步狀態(tài)沒有被任何線程獲取的時(shí)候
            if (!hasQueuedPredecessors() &&
                compareAndSetState(0, acquires)) { //這個(gè)點(diǎn)的主要處理邏輯就是:hasQueuedPredecessors判斷當(dāng)前線程所在的結(jié)點(diǎn)是否含有前驅(qū)結(jié)點(diǎn),                                  如果返回值為true表示有前驅(qū)結(jié)點(diǎn),那么當(dāng)前線程需要等待前驅(qū)結(jié)點(diǎn)中的線程獲取并釋放鎖之后才能獲取鎖,保證了FIFO
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        else if (current == getExclusiveOwnerThread()) { //支持重入的邏輯,和非公平鎖的實(shí)現(xiàn)原理相同
            int nextc = c + acquires;
            if (nextc < 0)
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        return false;
    }
}
//hasQueuedPredecessors的處理邏輯
public final boolean hasQueuedPredecessors() {
    // 簡(jiǎn)單而言,就是判斷當(dāng)前線程是否有前驅(qū)結(jié)點(diǎn)
    // 當(dāng)前結(jié)點(diǎn)含有前驅(qū)結(jié)點(diǎn)時(shí)候返回true;當(dāng)前結(jié)點(diǎn)為頭結(jié)點(diǎn)揮著隊(duì)列為空的時(shí)候返回false
    Node t = tail; // Read fields in reverse initialization order
    Node h = head;
    Node s;
    return h != t &&
        ((s = h.next) == null || s.thread != Thread.currentThread());
}

d)公平鎖和非公平鎖的測(cè)試

①測(cè)試目的

驗(yàn)證上面通過源碼分析的,非公平鎖在獲取鎖的時(shí)候會(huì)首先進(jìn)行搶鎖,在獲取鎖失敗后才會(huì)將當(dāng)前線程加入同步隊(duì)列隊(duì)尾中,而公平鎖則是符合請(qǐng)求的絕對(duì)順序,也就是會(huì)按照先來后到FIFO。在下面的代碼中我們使用一個(gè)靜態(tài)內(nèi)部類繼承了ReentrantLock并重寫等待隊(duì)列的方法,作為測(cè)試的ReentrantLock。然后創(chuàng)建5個(gè)線程,每個(gè)線程連續(xù)兩次去獲取鎖,分別測(cè)試公平鎖和非公平鎖的測(cè)試結(jié)果

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.junit.Test;

public class TestReentrantLock {
    /**
     * ReentrantLock的構(gòu)造方法
     * public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();}
     */
    private Lock fairLock = new ReentrantLock2(true);
    private Lock unFairLock = new ReentrantLock2(false);

    @Test
    public void testFair() throws InterruptedException {
        testLock(fairLock); //測(cè)試公平鎖
    }

    @Test
    public void testUnFair() throws InterruptedException {
        testLock(unFairLock); //測(cè)試非公平鎖
    }

    private void testLock(Lock lock) throws InterruptedException {
        for (int i = 0; i < 5; i++) {
            Thread thread = new Thread(new Job(lock)) {
                public String toString() {
                        return getName();
                }
            };
            thread.setName(i+"");
            thread.start();
        }
        Thread.sleep(12000);
    }

    private static class Job extends Thread {
        private Lock lock;
        public Job(Lock lock) {
            this.lock = lock;
        }
        @Override
        public void run() {
            //兩次打印當(dāng)前線程和等待隊(duì)列中的Threads
            for (int i = 0; i < 2; i++) {
                lock.lock(); //獲取鎖
                try {
                    Thread.sleep(1000);
                    System.out.println("當(dāng)前線程=>" + Thread.currentThread().getName() + " " +
                            "等待隊(duì)列中的線程=>" + ((ReentrantLock2)lock).getQueuedThreads());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock(); //釋放鎖
                }
            }
        }

    }

    private static class ReentrantLock2 extends ReentrantLock {
        public ReentrantLock2(boolean fair) {
            super(fair);
        }
        public Collection<Thread> getQueuedThreads() { //逆序打印等待隊(duì)列中的線程
            List<Thread> list = new ArrayList<Thread>(super.getQueuedThreads());
            Collections.reverse(list);
            return list;
        }
    }


}

②測(cè)試非公平鎖

由上面的測(cè)試結(jié)果簡(jiǎn)單的得到關(guān)于非公平鎖的一個(gè)結(jié)論:通過nofairTryAcquire方法可以得到這樣一個(gè)前提,當(dāng)一個(gè)線程請(qǐng)求一個(gè)鎖時(shí),判斷獲取成功的條件就是這個(gè)線程獲取到同步狀態(tài)就可以,那么某個(gè)剛剛釋放鎖的線程再次獲取到同步狀態(tài)的幾率就會(huì)更大一些(當(dāng)然實(shí)驗(yàn)中也出現(xiàn)并非連續(xù)兩次獲取這把鎖的情況,比如下面的測(cè)試結(jié)果)

③測(cè)試公平鎖

通過分析下面的測(cè)試結(jié)果,對(duì)于使用公平鎖而言,即便是同一個(gè)線程連續(xù)兩次獲取鎖釋放鎖,在第一次釋放鎖之后還是會(huì)被放在隊(duì)尾并從隊(duì)列頭部拿出線程進(jìn)行執(zhí)行。并沒有出現(xiàn)像非公平鎖那樣連續(xù)兩次獲取鎖的那種情況

④由上面的測(cè)試可以看出:非公平鎖可能導(dǎo)致在隊(duì)尾的線程饑餓,但是又因?yàn)橥粋€(gè)線程在釋放鎖的時(shí)候有更大的概率再次獲取到這把鎖,那么這樣的話線程的切換次數(shù)就會(huì)更少(這帶來的就是更大的吞吐量和開銷的減?。?。而雖然公平鎖的獲取嚴(yán)格按照FIFO的規(guī)則,但是線程切換的次數(shù)就會(huì)更多。

三、Synchronized

1、Synchronized作用對(duì)象

①對(duì)于普通方法,鎖的是當(dāng)前實(shí)例對(duì)象

②對(duì)于靜態(tài)同步方法,鎖的是類的Class對(duì)象

③對(duì)于同步代碼塊,鎖的是Synchronized括號(hào)中的對(duì)象

如下所示的三種情況

package cn.source.sync;

public class TestSync01 {
    private static int count = 0;
    private Object object = new Object();

    public void testSyn1() {
        //同步代碼塊(這里面是鎖臨界資源,即括號(hào)中的對(duì)象)
        synchronized (object) {
            System.out.println(Thread.currentThread().getName()
                +" count =" + count++);
        }
    }

    public void testSyn2() {
        //鎖當(dāng)前對(duì)象(相當(dāng)于普通同步方法)
        synchronized (this) {
            System.out.println(Thread.currentThread().getName()
                    +" count =" + count++);
        }
    }

    //普通同步方法:鎖當(dāng)前對(duì)象
    public synchronized void testSyn3() {
        System.out.println(Thread.currentThread().getName()
                +" count =" + count++);
    }

    //靜態(tài)同步方法,鎖的是當(dāng)前類型的類對(duì)象(即TestSync01.class)
    public static synchronized void testSyn4() {
        System.out.println(Thread.currentThread().getName()
                +" count =" + count++);
    }

    //下面的這種方式也是鎖當(dāng)前類型的類對(duì)象
    public static void testSyn5() {
        synchronized (TestSync01.class) {
            System.out.println(Thread.currentThread().getName()
                    +" count =" + count ++);
        }
    }
}

2、synchronized的實(shí)現(xiàn)原理

①Java 虛擬機(jī)中的同步(Synchronization)基于進(jìn)入和退出管程(Monitor)對(duì)象實(shí)現(xiàn)。同步代碼塊是使用monitorenter和monitorexit來實(shí)現(xiàn)的,同步方法 并不是由 monitor enter 和 monitor exit 指令來實(shí)現(xiàn)同步的,而是由方法調(diào)用指令讀取運(yùn)行時(shí)常量池中方法的 ACC_SYNCHRONIZED 標(biāo)志來隱式實(shí)現(xiàn)的。monitorenter指令是在編譯后插入同步代碼塊的起始位置,而monitorexit指令是在方法結(jié)束處和異常處,每個(gè)對(duì)象都有一個(gè)monitor與之關(guān)聯(lián),當(dāng)一個(gè)monitor被持有后它就會(huì)處于鎖定狀態(tài)。

②synchronized用的鎖是存在Java對(duì)象頭(非數(shù)組類型包括Mark Word、類型指針,數(shù)組類型多了數(shù)組長(zhǎng)度)里面的,對(duì)象頭中的Mark Word存儲(chǔ)對(duì)象的hashCode,分代年齡和鎖標(biāo)記位,類型指針指向?qū)ο蟮脑獢?shù)據(jù)信息,JVM通過這個(gè)指針確定該對(duì)象是那個(gè)類的實(shí)例等信息。

③當(dāng)在對(duì)象上加鎖的時(shí)候,數(shù)據(jù)是記錄在對(duì)象頭中,對(duì)象頭中的Mark Word里存儲(chǔ)的數(shù)據(jù)會(huì)隨著鎖標(biāo)志位的變化而變化(無鎖、輕量級(jí)鎖00、重量級(jí)鎖10、偏向鎖01)。當(dāng)執(zhí)行synchronized的同步方法或者同步代碼塊時(shí)候會(huì)在對(duì)象頭中記錄鎖標(biāo)記,鎖標(biāo)記指向的是monitor對(duì)象(也稱為管程或者監(jiān)視器鎖)的起始地址。由于每個(gè)對(duì)象都有一個(gè)monitor與之關(guān)聯(lián),monitor和與關(guān)聯(lián)的對(duì)象一起創(chuàng)建(當(dāng)線程試圖獲取鎖的時(shí)候)或銷毀,當(dāng)monitor被某個(gè)線程持有之后,就處于鎖定狀態(tài)。

④Hotspot虛擬機(jī)中的實(shí)現(xiàn),通過ObjectMonitor來實(shí)現(xiàn)的

如圖所示,ObjectMonitor中有兩個(gè)隊(duì)列(EntryList、WaitSet)以及鎖持有者Owner標(biāo)記,其中WaitSet是哪些調(diào)用wait方法之后被阻塞等待的線程隊(duì)列,EntryList是ContentionList中能有資格獲取鎖的線程隊(duì)列。當(dāng)多個(gè)線程并發(fā)訪問同一個(gè)同步代碼時(shí)候,首先會(huì)進(jìn)入EntryList,當(dāng)線程獲得鎖之后monitor中的Owner標(biāo)記會(huì)記錄此線程,并在該monitor中的計(jì)數(shù)器執(zhí)行遞增計(jì)算代表當(dāng)前鎖被持有鎖定,而沒有獲取到的線程繼續(xù)在EntryList中阻塞等待。如果線程調(diào)用了wait方法,則monitor中的計(jì)數(shù)器執(zhí)行賦0運(yùn)算,并且將Owner標(biāo)記賦值為null,代表當(dāng)前沒有線程持有鎖,同時(shí)調(diào)用wait方法的線程進(jìn)入WaitSet隊(duì)列中阻塞等待,直到持有鎖的執(zhí)行線程調(diào)用notify/notifyAll方法喚醒WaitSet中的線程,喚醒的線程進(jìn)入EntryList中等待鎖的獲取。除了使用wait方法可以將修改monitor的狀態(tài)之外,顯然持有鎖的線程的同步代碼塊執(zhí)行結(jié)束也會(huì)釋放鎖標(biāo)記,monitor中的Owner會(huì)被賦值為null,計(jì)數(shù)器賦值為0。如下圖所示

3、鎖的種類、升級(jí)和對(duì)比

a)鎖的種類

Java 中鎖的種類大致分為偏向鎖,自旋鎖,輕量級(jí)鎖,重量級(jí)鎖。鎖的使用方式為:先提供偏向鎖,如果不滿足的時(shí)候,升級(jí)為輕量級(jí)鎖,再不滿足,升級(jí)為重量級(jí)鎖。自旋鎖是一個(gè)過渡的鎖狀態(tài),不是一種實(shí)際的鎖類型。鎖只能升級(jí),不能降級(jí)。

b)鎖的升級(jí)

①偏向鎖

如果代碼中基本不可能出現(xiàn)多線程并發(fā)爭(zhēng)搶同一個(gè)鎖的時(shí)候,JVM 編譯代碼,解釋執(zhí)行的時(shí)候,會(huì)自動(dòng)的放棄同步信息,消除 synchronized 的同步代碼結(jié)果,使用鎖標(biāo)記的形式記錄鎖狀態(tài)。具體的實(shí)現(xiàn)方式大概就是:當(dāng)一個(gè)線程訪問同步塊并獲取鎖的時(shí)候,會(huì)在對(duì)象頭和棧幀的鎖記錄中存儲(chǔ)偏向的線程ID,之后線程在進(jìn)入和退出同步塊的時(shí)候不需要使用CAS進(jìn)行加鎖和解鎖,只需要測(cè)試對(duì)象頭中的MarkWord中是否存儲(chǔ)著當(dāng)前線程的偏向鎖;如果測(cè)試成功,就表示線程獲取鎖成功,如果測(cè)試失敗需要檢查對(duì)象頭中的MarkWord的偏向鎖表示是否設(shè)置為1,如果沒有設(shè)置就使用CAS競(jìng)爭(zhēng)鎖,設(shè)置了就以CAS方式將偏向鎖設(shè)置為當(dāng)前線程。在 Monitor 中有變量 ACC_SYNCHRONIZED。當(dāng)變量值使用的時(shí)候,代表偏向鎖鎖定。使用偏向鎖可以避免鎖的爭(zhēng)搶和鎖池狀態(tài)的維護(hù)。提高效率。

②輕量級(jí)鎖

當(dāng)偏向鎖不滿足,也就是有多線程并發(fā)訪問,鎖定同一個(gè)對(duì)象的時(shí)候,先提升為輕量級(jí)鎖。也是使用標(biāo)記 ACC_SYNCHRONIZED 標(biāo)記記錄的。ACC_UNSYNCHRONIZED 標(biāo)記記錄未獲取到鎖信息的線程。就是只有兩個(gè)線程爭(zhēng)搶鎖標(biāo)記的時(shí)候,優(yōu)先使用輕量級(jí)鎖。(自旋鎖)當(dāng)獲取鎖的過程中,未獲取到。為了提高效率,JVM 自動(dòng)執(zhí)行若干次空循環(huán),再次申請(qǐng)鎖,而不是進(jìn)入阻塞狀態(tài)的情況。稱為自旋鎖。自旋鎖提高效率就是避免線程狀態(tài)的變更

③重量級(jí)鎖

在自旋過程中,為了避免無用的自旋(比如獲得鎖的線程被阻塞住了),鎖就會(huì)被升級(jí)為重量級(jí)鎖。在重量級(jí)鎖的狀態(tài)下,其他線程視圖獲取鎖的時(shí)候都會(huì)被阻塞住,只有持有鎖的線程釋放鎖之后才會(huì)喚醒那些阻塞的線程,這些線程就開始競(jìng)爭(zhēng)鎖。

4、關(guān)于synchronized的其他說明

a)關(guān)于同步方法和非同步方法

同步方法只影響 鎖定同一個(gè)鎖對(duì)象的同步方法,不影響非同步方法被其他線程調(diào)用,也不影響其他所資源的同步方法(簡(jiǎn)單理解就是鎖的不是同一個(gè)資源,就不會(huì)影響);

b)synchronized是可重入的

同一個(gè)線程,多次調(diào)用同步代碼,鎖定同一個(gè)對(duì)象,是可重入的;

c)關(guān)于同步的繼承問題

同一個(gè)線程中,子類同步方法覆蓋父類的同步方法,可以指定調(diào)用父類的同步方法(相當(dāng)于鎖的重入)

d)鎖與異常

當(dāng)同步方法出現(xiàn)異常的時(shí)候會(huì)自動(dòng)釋放鎖,不會(huì)影響其他線程的執(zhí)行

e)synchronized鎖的是對(duì)象,而不是引用

同步代碼一旦加鎖之后會(huì)有一個(gè)臨時(shí)鎖引用執(zhí)行鎖對(duì)象,和真實(shí)的引用無直接關(guān)聯(lián),在鎖釋放之前,修改鎖對(duì)象引用不會(huì)影響同步代碼塊的執(zhí)行

f)synchronized中的常量問題

在定義同步代碼塊的時(shí)候,不要使用常量對(duì)象作為鎖對(duì)象

以上就是詳解Java中的鎖Lock和synchronized的詳細(xì)內(nèi)容,更多關(guān)于Java Lock synchronized的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java并發(fā)編程之LongAdder執(zhí)行情況解析

    Java并發(fā)編程之LongAdder執(zhí)行情況解析

    這篇文章主要為大家介紹了Java并發(fā)編程之LongAdder執(zhí)行情況解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-04-04
  • 我從jdk1.8升級(jí)到j(luò)dk11所遇到的坑都有這些

    我從jdk1.8升級(jí)到j(luò)dk11所遇到的坑都有這些

    這篇文章主要介紹了從jdk1.8升級(jí)到j(luò)dk11將會(huì)遇到的一些坑,本文給大家分享解決方案對(duì)大家的學(xué)習(xí)或工作具有參考借鑒價(jià)值,對(duì)jdk1.8升級(jí)到j(luò)dk11相關(guān)知識(shí)感興趣的朋友,快來看看吧
    2021-08-08
  • MyBatis中#和$的區(qū)別小結(jié)

    MyBatis中#和$的區(qū)別小結(jié)

    ${} 和 #{} 都是 MyBatis 中用來替換參數(shù)的,它們都可以將用戶傳遞過來的參數(shù),替換到 MyBatis 最終生成的 SQL 中,但它們區(qū)別卻是很大的,接下來我們一起來看
    2023-09-09
  • springboot restTemplate連接池整合方式

    springboot restTemplate連接池整合方式

    這篇文章主要介紹了springboot restTemplate連接池整合方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • 分析ABA問題的本質(zhì)及其解決辦法

    分析ABA問題的本質(zhì)及其解決辦法

    CAS的全稱是compare and swap,它是java同步類的基礎(chǔ),java.util.concurrent中的同步類基本上都是使用CAS來實(shí)現(xiàn)其原子性的。本文將介紹ABA問題的本質(zhì)及其解決辦法。
    2021-06-06
  • Springboot實(shí)現(xiàn)人臉識(shí)別與WebSocket長(zhǎng)連接的實(shí)現(xiàn)代碼

    Springboot實(shí)現(xiàn)人臉識(shí)別與WebSocket長(zhǎng)連接的實(shí)現(xiàn)代碼

    這篇文章主要介紹了Springboot實(shí)現(xiàn)人臉識(shí)別與WebSocket長(zhǎng)連接的實(shí)現(xiàn),本文通過示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2023-11-11
  • Java如何實(shí)現(xiàn)樹的同構(gòu)?

    Java如何實(shí)現(xiàn)樹的同構(gòu)?

    今天給大家?guī)淼氖顷P(guān)于Java的相關(guān)知識(shí),文章圍繞著Java如何實(shí)現(xiàn)樹的同構(gòu)展開,文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下
    2021-06-06
  • Java測(cè)試框架Mockito的簡(jiǎn)明教程

    Java測(cè)試框架Mockito的簡(jiǎn)明教程

    這篇文章主要介紹了Java測(cè)試框架Mockito的簡(jiǎn)明教程,Mock 測(cè)試是單元測(cè)試的重要方法之一。本文介紹了基于 Java 語言的 Mock 測(cè)試框架 – Mockito 的使用。,需要的朋友可以參考下
    2019-06-06
  • Java接口默認(rèn)方法帶來的問題分析【二義性問題】

    Java接口默認(rèn)方法帶來的問題分析【二義性問題】

    這篇文章主要介紹了Java接口默認(rèn)方法帶來的問題,結(jié)合實(shí)例形式分析了java接口帶來的二義性問題,需要的朋友可以參考下
    2019-08-08
  • mybatis使用foreach查詢不出結(jié)果也不報(bào)錯(cuò)的問題

    mybatis使用foreach查詢不出結(jié)果也不報(bào)錯(cuò)的問題

    這篇文章主要介紹了mybatis使用foreach查詢不出結(jié)果也不報(bào)錯(cuò)的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03

最新評(píng)論