欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解Java中的鎖Lock和synchronized

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

一、Lock接口

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

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

b)Lock接口:Lock接口提供了與synchronized相似的同步功能,和synchronized(隱式的獲取和釋放鎖,主要體現(xiàn)在線程進(jìn)入同步代碼塊之前需要獲取鎖退出同步代碼塊需要釋放鎖)不同的是,Lock在使用的時(shí)候是顯示的獲取和釋放鎖。雖然Lock接口缺少了synchronized隱式獲取釋放鎖的便捷性,但是對于鎖的操作具有更強(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í),也會導(dǎo)致鎖的無故釋放
try {
}finally {
    lock.unlock(); //放在finally代碼塊中,保證鎖一定會被釋放
}

3、Lock接口的方法

public interface Lock {

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

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

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

    /**
     * 超時(shí)的獲取鎖,下面的三種情況會返回
     * ①當(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)用該方法之后會釋放鎖
     */
    Condition newCondition();
}

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

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

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

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

二、重入鎖

1、重入鎖的概念

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

2、ReentrantLock

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

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

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

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

③ReentrantLock的可重入測試

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) // 對同步狀態(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; //對同一線程而言,就是減去相應(yīng)的獲取次數(shù)
        if (Thread.currentThread() != getExclusiveOwnerThread())
            throw new IllegalMonitorStateException();
        boolean free = false; //返回值
        if (c == 0) { //只有該線程將獲取的次數(shù)全部釋放之后,才會返回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方法增加了對于同一線程再次獲取同步狀態(tài)的邏輯處理(通過判斷當(dāng)前線程是否為已經(jīng)同步狀態(tài)的持有者,來決定是否能夠再次獲取同步狀態(tài),如果當(dāng)前線程是已經(jīng)獲取到同步狀態(tài)的那個(gè)線程,那么就能夠獲取成功,并且同時(shí)以CAS的方式修改state的值)

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

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

①非公平鎖

公平和非公平是針對于獲取鎖而言的,對于公平鎖而言獲取鎖應(yīng)該遵循FIFO原則,上面我們通過源碼分析了非公平鎖的實(shí)現(xiàn)(對于非公平鎖而言,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() {
    // 簡單而言,就是判斷當(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)公平鎖和非公平鎖的測試

①測試目的

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

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

    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;
        }
    }


}

②測試非公平鎖

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

③測試公平鎖

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

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

三、Synchronized

1、Synchronized作用對象

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

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

③對于同步代碼塊,鎖的是Synchronized括號中的對象

如下所示的三種情況

package cn.source.sync;

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

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

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

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

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

    //下面的這種方式也是鎖當(dāng)前類型的類對象
    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)對象實(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è)對象都有一個(gè)monitor與之關(guān)聯(lián),當(dāng)一個(gè)monitor被持有后它就會處于鎖定狀態(tài)。

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

③當(dāng)在對象上加鎖的時(shí)候,數(shù)據(jù)是記錄在對象頭中,對象頭中的Mark Word里存儲的數(shù)據(jù)會隨著鎖標(biāo)志位的變化而變化(無鎖、輕量級鎖00、重量級鎖10、偏向鎖01)。當(dāng)執(zhí)行synchronized的同步方法或者同步代碼塊時(shí)候會在對象頭中記錄鎖標(biāo)記,鎖標(biāo)記指向的是monitor對象(也稱為管程或者監(jiān)視器鎖)的起始地址。由于每個(gè)對象都有一個(gè)monitor與之關(guān)聯(lián),monitor和與關(guān)聯(lián)的對象一起創(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í)候,首先會進(jìn)入EntryList,當(dāng)線程獲得鎖之后monitor中的Owner標(biāo)記會記錄此線程,并在該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é)束也會釋放鎖標(biāo)記,monitor中的Owner會被賦值為null,計(jì)數(shù)器賦值為0。如下圖所示

3、鎖的種類、升級和對比

a)鎖的種類

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

b)鎖的升級

①偏向鎖

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

②輕量級鎖

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

③重量級鎖

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

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

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

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

b)synchronized是可重入的

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

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

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

d)鎖與異常

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

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

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

f)synchronized中的常量問題

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

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

相關(guān)文章

  • 詳解Java編程中JavaMail API的使用

    詳解Java編程中JavaMail API的使用

    這篇文章主要介紹了詳解Java編程中JavaMail API的使用,通過JavaMail可以實(shí)現(xiàn)豐富的郵件類相關(guān)功能,需要的朋友可以參考下
    2015-11-11
  • SpringBoot中分頁插件PageHelper的使用詳解

    SpringBoot中分頁插件PageHelper的使用詳解

    分頁查詢是為了高效展示大量數(shù)據(jù),通過分頁將數(shù)據(jù)劃分為多個(gè)部分逐頁展示,原生方法需手動計(jì)算數(shù)據(jù)起始行,而使用PageHelper插件則簡化這一過程,本文給大家介紹SpringBoot中分頁插件PageHelper的使用,感興趣的朋友一起看看吧
    2024-09-09
  • java算法導(dǎo)論之FloydWarshall算法實(shí)現(xiàn)代碼

    java算法導(dǎo)論之FloydWarshall算法實(shí)現(xiàn)代碼

    這篇文章主要介紹了算法導(dǎo)論之FloydWarshall算法實(shí)現(xiàn)代碼的相關(guān)資料,需要的朋友可以參考下
    2017-05-05
  • Gradle的SpringBoot項(xiàng)目構(gòu)建圖解

    Gradle的SpringBoot項(xiàng)目構(gòu)建圖解

    這篇文章主要介紹了Gradle的SpringBoot項(xiàng)目構(gòu)建圖解,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • Java指令重排引發(fā)問題及解決方案

    Java指令重排引發(fā)問題及解決方案

    指令重排是JVM在解釋執(zhí)行Java代碼時(shí)對指令順序進(jìn)行重新排列的一種優(yōu)化技術(shù),本文主要介紹了Java指令重排引發(fā)問題及解決方案,感興趣的可以了解一下
    2023-08-08
  • 解決java 分割字符串成數(shù)組時(shí),小圓點(diǎn)不能直接進(jìn)行分割的問題

    解決java 分割字符串成數(shù)組時(shí),小圓點(diǎn)不能直接進(jìn)行分割的問題

    這篇文章主要介紹了解決java 分割字符串成數(shù)組時(shí),小圓點(diǎn)不能直接進(jìn)行分割的問題,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • 使用maven對springboot項(xiàng)目進(jìn)行瘦身分離jar的多種處理方案

    使用maven對springboot項(xiàng)目進(jìn)行瘦身分離jar的多種處理方案

    springboot項(xiàng)目打包一般我們都使用它自帶的spring-boot-maven-plugin插件,這個(gè)插件默認(rèn)情況下,會把所有的依賴包全部壓縮到一個(gè)jar里面,今天給大家分享幾種方案來如何減小我們的打包文件,需要的朋友可以參考下
    2024-02-02
  • Eclipse插件大全 挑選最牛的TOP30(全)

    Eclipse插件大全 挑選最牛的TOP30(全)

    ?“Eclipse最牛的30個(gè)插件”不知道看官們是否了解,風(fēng)少俠特意翻譯出來奉獻(xiàn)給各位,希望大家喜歡
    2013-02-02
  • 解決spring boot環(huán)境切換失效的問題

    解決spring boot環(huán)境切換失效的問題

    這篇文章主要介紹了解決spring boot環(huán)境切換失效的問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • spring mvc4中相關(guān)注解的詳細(xì)講解教程

    spring mvc4中相關(guān)注解的詳細(xì)講解教程

    這篇文章主要給大家介紹了關(guān)于spring mvc4中相關(guān)注解的相關(guān)資料,其中詳細(xì)介紹了關(guān)于@Controller、@RequestMapping、@RathVariable、@RequestParam及@RequestBody等等注解的相關(guān)內(nèi)容,需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-06-06

最新評論