最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解java中各類(lèi)鎖的機(jī)制

 更新時(shí)間:2021年12月24日 11:21:43   作者:碼農(nóng)研究僧  
這篇文章為大家總結(jié)了java中常見(jiàn)的鎖(互斥鎖、讀寫(xiě)鎖、公平鎖與非公平鎖等)的機(jī)制以及如何使用,文中示例代碼講解詳細(xì),需要的可以學(xué)習(xí)一下

前言

總結(jié)java常見(jiàn)的鎖

區(qū)分各個(gè)鎖機(jī)制以及如何使用

使用方法 鎖名
考察線程是否要鎖住同步資源 樂(lè)觀鎖和悲觀鎖
鎖住同步資源后,要不要阻塞 不阻塞可以使用自旋鎖
一個(gè)線程多個(gè)流程獲取同一把鎖 可重入鎖
多個(gè)線程公用一把鎖 讀寫(xiě)鎖(寫(xiě)的共享鎖)
多個(gè)線程競(jìng)爭(zhēng)要不要排隊(duì) 公平鎖與非公平鎖

1. 樂(lè)觀鎖與悲觀鎖

悲觀鎖:不能同時(shí)進(jìn)行多人,執(zhí)行的時(shí)候先上鎖。傳統(tǒng)的關(guān)系型數(shù)據(jù)庫(kù)里邊就用到了很多這種鎖機(jī)制,比如行鎖,表鎖等,讀鎖,寫(xiě)鎖等,都是在做操作之前先上鎖

樂(lè)觀鎖:通過(guò)版本號(hào)一致與否,即給數(shù)據(jù)加上版本,同步更新數(shù)據(jù)以及加上版本號(hào)。不會(huì)上鎖,判斷版本號(hào),可以多人操作,類(lèi)似生活中的搶票。每次去拿數(shù)據(jù)的時(shí)候都認(rèn)為別人不會(huì)修改,所以不會(huì)上鎖,但是在更新的時(shí)候會(huì)判斷一下在此期間別人有沒(méi)有去更新這個(gè)數(shù)據(jù),可以使用版本號(hào)等機(jī)制。樂(lè)觀鎖適用于多讀的應(yīng)用類(lèi)型,這樣可以提高吞吐量。Redis就是利用這種check-and-set機(jī)制實(shí)現(xiàn)事務(wù)的

(樂(lè)觀鎖可以使用版本號(hào)機(jī)制和CAS算法實(shí)現(xiàn))

通過(guò)具體案例演示悲觀鎖和樂(lè)觀鎖

在redis框架中

執(zhí)行multi之前,執(zhí)行命令watch

具體格式如下

watch key1 [key2]

具體代碼格式如下

127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> set add 100
OK
127.0.0.1:6379> watch add
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> incrby add 20
QUEUED
127.0.0.1:6379(TX)> exec
1) (integer) 120
127.0.0.1:6379> 

flushdb是清空數(shù)據(jù)庫(kù)

但如果在另一個(gè)服務(wù)器上,輸入exec,會(huì)顯示出錯(cuò)

因?yàn)橛玫氖菢?lè)觀鎖,被修改了之后版本會(huì)發(fā)生改變

總的來(lái)說(shuō):

悲觀鎖:?jiǎn)为?dú)每個(gè)人完成事情的時(shí)候,執(zhí)行上鎖解鎖。解決并發(fā)中的問(wèn)題,不支持并發(fā)操作,只能一個(gè)一個(gè)操作,效率低

樂(lè)觀鎖:每執(zhí)行一件事情,都會(huì)比較數(shù)據(jù)版本號(hào),誰(shuí)先提交,誰(shuí)先提交版本號(hào)

2. 公平鎖與非公平鎖

公平鎖:先來(lái)先到

非公平鎖:不是按照順序,可插隊(duì)

  • 公平鎖:效率相對(duì)低
  • 非公平鎖:效率高,但是線程容易餓死

通過(guò)這個(gè)函數(shù)Lock lock = new ReentrantLock(true);。創(chuàng)建一個(gè)可重入鎖,true 表示公平鎖,false 表示非公平鎖。默認(rèn)非公平鎖

通過(guò)查看源碼

帶有參數(shù)的ReentrantLock(true)為公平鎖

ReentrantLock(false)為非公平鎖

主要是調(diào)用NonfairSync()與FairSync()

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

具體其非公平鎖與公平鎖的源碼

查看公平鎖的源碼

static final class FairSync extends Sync {
   private static final long serialVersionUID = -3000897897090466540L;

  /**
  * Acquires only if reentrant or queue is empty.
   */
  final boolean initialTryLock() {
   Thread current = Thread.currentThread();
   int c = getState();
   if (c == 0) {
   if (!hasQueuedThreads() && compareAndSetState(0, 1)) {
     setExclusiveOwnerThread(current);
      return true;
    }
    } else if (getExclusiveOwnerThread() == current) {
      if (++c < 0) // overflow
          throw new Error("Maximum lock count exceeded");
         setState(c);
         return true;
       }
    return false;
}

通過(guò)代碼實(shí)例具體操作

//第一步  創(chuàng)建資源類(lèi),定義屬性和和操作方法
class LTicket {
    //票數(shù)量
    private int number = 30;

    //創(chuàng)建可重入鎖
    private final ReentrantLock lock = new ReentrantLock(true);
    //賣(mài)票方法
    public void sale() {
        //上鎖
        lock.lock();
        try {
            //判斷是否有票
            if(number > 0) {
                System.out.println(Thread.currentThread().getName()+" :賣(mài)出"+(number--)+" 剩余:"+number);
            }
        } finally {
            //解鎖
            lock.unlock();
        }
    }
}

public class LSaleTicket {
    //第二步 創(chuàng)建多個(gè)線程,調(diào)用資源類(lèi)的操作方法
    //創(chuàng)建三個(gè)線程
    public static void main(String[] args) {

        LTicket ticket = new LTicket();

new Thread(()-> {
    for (int i = 0; i < 40; i++) {
        ticket.sale();
    }
},"AA").start();

        new Thread(()-> {
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        },"BB").start();

        new Thread(()-> {
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        },"CC").start();
    }
}

結(jié)果截圖如下

都是A線程執(zhí)行,而B(niǎo)C線程都沒(méi)執(zhí)行到,出現(xiàn)了非公平鎖

具體改變其設(shè)置可以通過(guò)可重入鎖中的一個(gè)有參構(gòu)造方法

修改代碼為private final ReentrantLock lock = new ReentrantLock(true);

代碼截圖為

3. 可重入鎖

可重入鎖也叫遞歸鎖

而且有了可重入鎖之后,破解第一把之后就可以一直進(jìn)入到內(nèi)層結(jié)構(gòu)

Object o = new Object();
new Thread(()->{
    synchronized(o) {
        System.out.println(Thread.currentThread().getName()+" 外層");

        synchronized (o) {
            System.out.println(Thread.currentThread().getName()+" 中層");

            synchronized (o) {
                System.out.println(Thread.currentThread().getName()+" 內(nèi)層");
            }
        }
    }

},"t1").start();

synchronized (o)代表鎖住當(dāng)前{ }內(nèi)的代碼塊

以上都是synchronized鎖機(jī)制

下面講解lock鎖機(jī)制

public class SyncLockDemo {

    public synchronized void add() {
        add();
    }

    public static void main(String[] args) {
        //Lock演示可重入鎖
        Lock lock = new ReentrantLock();
        //創(chuàng)建線程
        new Thread(()->{
            try {
                //上鎖
                lock.lock();
                System.out.println(Thread.currentThread().getName()+" 外層");

                try {
                    //上鎖
                    lock.lock();
                    System.out.println(Thread.currentThread().getName()+" 內(nèi)層");
                }finally {
                    //釋放鎖
                    lock.unlock();
                }
            }finally {
                //釋放做
                lock.unlock();
            }
        },"t1").start();

        //創(chuàng)建新線程
        new Thread(()->{
            lock.lock();
            System.out.println("aaaa");
            lock.unlock();
        },"aa").start();
        }
 }

在同一把鎖中的嵌套鎖,內(nèi)部嵌套鎖沒(méi)解鎖還是可以輸出,但是如果跳出該線程,執(zhí)行另外一個(gè)線程就會(huì)造成死鎖

要把握上鎖與解鎖的概念,都要寫(xiě)上

4. 讀寫(xiě)鎖(共享鎖與獨(dú)占鎖)

讀鎖是共享鎖,寫(xiě)鎖是獨(dú)占鎖

  • 共享鎖的一種具體實(shí)現(xiàn)
  • 讀寫(xiě)鎖管理一組鎖,一個(gè)是只讀的鎖,一個(gè)是寫(xiě)鎖。

讀寫(xiě)鎖:一個(gè)資源可以被多個(gè)讀線程訪問(wèn),也可以被一個(gè)寫(xiě)線程訪問(wèn),但不能同時(shí)存在讀寫(xiě)線程,讀寫(xiě)互斥,讀讀共享(寫(xiě)鎖獨(dú)占,讀鎖共享,寫(xiě)鎖優(yōu)先級(jí)高于讀鎖)

讀寫(xiě)鎖ReentrantReadWriteLock

讀鎖為ReentrantReadWriteLock.ReadLock,readLock()方法

寫(xiě)鎖為ReentrantReadWriteLock.WriteLock,writeLock()方法

創(chuàng)建讀寫(xiě)鎖對(duì)象private ReadWriteLock rwLock = new ReentrantReadWriteLock();

寫(xiě)鎖 加鎖 rwLock.writeLock().lock();,解鎖為rwLock.writeLock().unlock();

讀鎖 加鎖rwLock.readLock().lock();,解鎖為rwLock.readLock().unlock();

案例分析:

模擬多線程在map中取數(shù)據(jù)和讀數(shù)據(jù)

完整代碼如下

//資源類(lèi)
class MyCache {
    //創(chuàng)建map集合
    private volatile Map<String,Object> map = new HashMap<>();

    //創(chuàng)建讀寫(xiě)鎖對(duì)象
    private ReadWriteLock rwLock = new ReentrantReadWriteLock();

    //放數(shù)據(jù)
    public void put(String key,Object value) {
        //添加寫(xiě)鎖
        rwLock.writeLock().lock();

        try {
            System.out.println(Thread.currentThread().getName()+" 正在寫(xiě)操作"+key);
            //暫停一會(huì)
            TimeUnit.MICROSECONDS.sleep(300);
            //放數(shù)據(jù)
            map.put(key,value);
            System.out.println(Thread.currentThread().getName()+" 寫(xiě)完了"+key);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //釋放寫(xiě)鎖
            rwLock.writeLock().unlock();
        }
    }

    //取數(shù)據(jù)
    public Object get(String key) {
        //添加讀鎖
        rwLock.readLock().lock();
        Object result = null;
        try {
            System.out.println(Thread.currentThread().getName()+" 正在讀取操作"+key);
            //暫停一會(huì)
            TimeUnit.MICROSECONDS.sleep(300);
            result = map.get(key);
            System.out.println(Thread.currentThread().getName()+" 取完了"+key);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //釋放讀鎖
            rwLock.readLock().unlock();
        }
        return result;
    }
}

public class ReadWriteLockDemo {
    public static void main(String[] args) throws InterruptedException {
        MyCache myCache = new MyCache();
        //創(chuàng)建線程放數(shù)據(jù)
        for (int i = 1; i <=5; i++) {
            final int num = i;
            new Thread(()->{
                myCache.put(num+"",num+"");
            },String.valueOf(i)).start();
        }

        TimeUnit.MICROSECONDS.sleep(300);

        //創(chuàng)建線程取數(shù)據(jù)
        for (int i = 1; i <=5; i++) {
            final int num = i;
            new Thread(()->{
                myCache.get(num+"");
            },String.valueOf(i)).start();
        }
    }
}

5. 互斥鎖

互斥鎖是獨(dú)占鎖的一種常規(guī)實(shí)現(xiàn),是指某一資源同時(shí)只允許一個(gè)訪問(wèn)者對(duì)其進(jìn)行訪問(wèn),具有唯一性和排它性

pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;//創(chuàng)建互斥鎖并初始化

pthread_mutex_lock(&mutex);//對(duì)線程上鎖,此時(shí)其他線程阻塞等待該線程釋放鎖

//要執(zhí)行的代碼段

pthread_mutex_unlock(&mutex);//執(zhí)行完后釋放鎖

6. 自旋鎖

查看百度百科的解釋?zhuān)唧w如下 :

它是為實(shí)現(xiàn)保護(hù)共享資源而提出一種鎖機(jī)制。其實(shí),自旋鎖與互斥鎖比較類(lèi)似,它們都是為了解決對(duì)某項(xiàng)資源的互斥使用。無(wú)論是互斥鎖,還是自旋鎖,在任何時(shí)刻,最多只能有一個(gè)保持者,也就說(shuō),在任何時(shí)刻最多只能有一個(gè)執(zhí)行單元獲得鎖。但是兩者在調(diào)度機(jī)制上略有不同。對(duì)于互斥鎖,如果資源已經(jīng)被占用,資源申請(qǐng)者只能進(jìn)入睡眠狀態(tài)。但是自旋鎖不會(huì)引起調(diào)用者睡眠,如果自旋鎖已經(jīng)被別的執(zhí)行單元保持,調(diào)用者就一直循環(huán)在那里看是否該自旋鎖的保持者已經(jīng)釋放了鎖,"自旋"一詞就是因此而得名

通俗的來(lái)說(shuō)就是一個(gè)線程在獲取鎖的時(shí)候,如果鎖已經(jīng)被其它線程獲取,那么該線程將循環(huán)等待,然后不斷的判斷鎖是否能夠被成功獲取,直到獲取到鎖才會(huì)退出循環(huán)。獲取鎖的線程一直處于活躍狀態(tài),但是并沒(méi)有執(zhí)行任何有效的任務(wù)。

其特點(diǎn):

  1. 持有鎖時(shí)間等待過(guò)長(zhǎng),消耗CPU
  2. 無(wú)法滿足等待時(shí)間最長(zhǎng)的線程優(yōu)先獲取鎖。不公平的鎖就會(huì)存在“線程饑餓”問(wèn)題
  3. 自旋鎖不會(huì)使線程狀態(tài)發(fā)生切換,處于用戶態(tài)(不會(huì)到內(nèi)核態(tài)進(jìn)行線程的狀態(tài)轉(zhuǎn)換),一直都是活躍,不會(huì)使線程進(jìn)入阻塞狀態(tài),減少了不必要的上下文切換,執(zhí)行速度快。

其模擬算法如下

do{
	b=1;
	while(b){
		lock(bus);
		b = test_and_set(&lock);
		unlock(bus);
	}
	//臨界區(qū)
	//lock = 0;
	//其余部分
}while(1)

7. 無(wú)鎖 / 偏向鎖 / 輕量級(jí)鎖 / 重量級(jí)鎖

  • 無(wú)鎖:沒(méi)有對(duì)資源進(jìn)行鎖定,所有的線程都能訪問(wèn)并修改同一個(gè)資源,但同時(shí)只有一個(gè)線程能修改成功
  • 偏向鎖:是指一段同步代碼一直被一個(gè)線程所訪問(wèn),那么該線程會(huì)自動(dòng)獲取鎖,降低獲取鎖的代價(jià)
  • 輕量級(jí)鎖:鎖是偏向鎖的時(shí)候,被另外的線程所訪問(wèn),偏向鎖就會(huì)升級(jí)為輕量級(jí)鎖,其他線程會(huì)通過(guò)自旋的形式嘗試獲取鎖,不會(huì)阻塞,從而提高性能
  • 重量級(jí)鎖:線程并發(fā)加劇,線程的自旋超過(guò)了一定次數(shù),或者一個(gè)線程持有鎖,一個(gè)線程在自旋,還有線程要訪問(wèn)

以上就是詳解java中各類(lèi)鎖的機(jī)制的詳細(xì)內(nèi)容,更多關(guān)于java鎖的機(jī)制的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論

彭水| 黄冈市| 高要市| 那曲县| 佛山市| 尚义县| 龙川县| 盘山县| 延川县| 普安县| 凤台县| 庆元县| 措美县| 息烽县| 邯郸县| 永嘉县| 汝阳县| 长宁区| 凤城市| 广丰县| 井冈山市| 微博| 三都| 同仁县| 平定县| 克什克腾旗| 莱芜市| 林州市| 比如县| 呼玛县| 临沂市| 拜城县| 安岳县| 博湖县| 磐安县| 咸丰县| 潞城市| 宜都市| 广灵县| 应城市| 凉城县|