首頁 > Java > java教程 > 主體

java中各類鎖的機制是什麼

王林
發布: 2023-04-20 08:34:12
轉載
1112 人瀏覽過

前言

總結java常見的鎖定

區分各個鎖定機制以及如何使用

##使用方法鎖定名稱檢視執行緒是否要鎖定同步資源樂觀鎖定與悲觀鎖定鎖住同步資源後,要不要阻塞不阻塞可以使用自旋鎖定#一個執行緒多個流程取得同一把鎖定 #可重入鎖定多個執行緒公用一把鎖定讀取寫入鎖定(寫入的共享鎖定)# #多個執行緒競爭要不要排隊公平鎖定與非公平鎖定
####

1. 樂觀鎖與悲觀鎖

悲觀鎖:不能同時進行多人,執行的時候先上鎖。傳統的關係型資料庫裡邊就用到了很多這種鎖機制,例如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖

樂觀鎖:透過版本號一致與否,即為資料加上版本,同步更新資料以及加上版本號碼。不會上鎖,判斷版本號,可以多人操作,類似生活中的搶票。每次去拿數據的時候都認為別人不會修改,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個數據,可以使用版本號等機制。樂觀鎖適用於多讀取的應用類型,這樣可以提高吞吐量。 Redis就是利用這種check-and-set機制實現事務的

(樂觀鎖可以使用版本號機制和CAS演算法實現)

java中各類鎖的機制是什麼

透過具體案例示範悲觀鎖定和樂觀鎖定

在redis框架中

執行multi之前,執行指令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是清空資料庫

java中各類鎖的機制是什麼

但如果在另一個伺服器上,輸入exec,會顯示出錯

因為用的是樂觀鎖,被修改了之後版本會發生改變

總的來說:

悲觀鎖:單獨每個人完成事情的時候,執行上鎖解鎖。解決並發中的問題,不支援並發操作,只能一個一個操作,效率低

樂觀鎖:每執行一件事情,都會比較資料版本號,誰先提交,誰先提交版本號

2. 公平鎖定與非公平鎖定

公平鎖定:先來先到

非公平鎖定:不是依照順序,可插隊

  • 公平鎖定:效率相對低

  • 非公平鎖定:效率高,但是執行緒容易餓死

透過這個函數Lock lock = new ReentrantLock(true);。建立一個可重入鎖,true 表示公平鎖,false 表示非公平鎖。預設非公平鎖定

透過查看原始碼

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

ReentrantLock(false)為非公平鎖定

java中各類鎖的機制是什麼

##主要是呼叫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;
}
登入後複製

透過程式碼實例具體操作

//第一步  创建资源类,定义属性和和操作方法
class LTicket {
    //票数量
    private int number = 30;

    //创建可重入锁
    private final ReentrantLock lock = new ReentrantLock(true);
    //卖票方法
    public void sale() {
        //上锁
        lock.lock();
        try {
            //判断是否有票
            if(number > 0) {
                System.out.println(Thread.currentThread().getName()+" :卖出"+(number--)+" 剩余:"+number);
            }
        } finally {
            //解锁
            lock.unlock();
        }
    }
}

public class LSaleTicket {
    //第二步 创建多个线程,调用资源类的操作方法
    //创建三个线程
    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();
    }
}
登入後複製

結果截圖如下

java中各類鎖的機制是什麼

都是A執行緒執行,而BC執行緒都沒執行到,出現了非公平鎖定

具體改變其設定可以透過可重入鎖中的一個有參構造方法

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

#程式碼截圖為

3. 可重入鎖

可重入鎖也叫遞歸鎖

而且有了可重入鎖之後,破解第一把之後就可以一直進入到內層結構java中各類鎖的機制是什麼

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()+" 内层");
            }
        }
    }

},"t1").start();
登入後複製

synchronized (o)代表鎖住目前{ }內的程式碼區塊

以上都是synchronized鎖定機制

#下面講解lock鎖定機制
    public class SyncLockDemo {
    
        public synchronized void add() {
            add();
        }
    
        public static void main(String[] args) {
            //Lock演示可重入锁
            Lock lock = new ReentrantLock();
            //创建线程
            new Thread(()->{
                try {
                    //上锁
                    lock.lock();
                    System.out.println(Thread.currentThread().getName()+" 外层");
    
                    try {
                        //上锁
                        lock.lock();
                        System.out.println(Thread.currentThread().getName()+" 内层");
                    }finally {
                        //释放锁
                        lock.unlock();
                    }
                }finally {
                    //释放做
                    lock.unlock();
                }
            },"t1").start();
    
            //创建新线程
            new Thread(()->{
                lock.lock();
                System.out.println("aaaa");
                lock.unlock();
            },"aa").start();
            }
     }
    登入後複製
  • 在同一把鎖中的嵌套鎖,內部嵌套鎖沒解鎖還是可以輸出,但是如果跳出該線程,執行另外一個線程就會造成死鎖

    #要掌握上鎖與解鎖的概念,都要寫上
  • 4. 讀寫鎖(共享鎖與獨佔鎖)

#讀鎖是共享鎖,寫鎖是獨佔鎖

共享鎖的一種具體實作

讀寫鎖管理一組鎖,一個是只讀的鎖,一個是寫鎖。

讀寫鎖定:一個資源可以被多個讀取線程訪問,也可以被一個寫線程訪問,但不能同時存在讀寫線程,讀寫互斥,讀讀共享(寫鎖獨佔,讀鎖共享,寫鎖優先權高於讀鎖)

讀寫鎖ReentrantReadWriteLock

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

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

建立讀寫鎖定物件private ReadWriteLock rwLock = new ReentrantReadWriteLock();

寫入鎖定rwLock.writeLock().lock(writeLock().lock(writeLock().lock(writeLock().lock( );,解鎖為rwLock.writeLock().unlock();

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

案例分析:

###模擬多執行緒在map中取資料和讀取資料######完整程式碼如下###
//资源类
class MyCache {
    //创建map集合
    private volatile Map<String,Object> map = new HashMap<>();

    //创建读写锁对象
    private ReadWriteLock rwLock = new ReentrantReadWriteLock();

    //放数据
    public void put(String key,Object value) {
        //添加写锁
        rwLock.writeLock().lock();

        try {
            System.out.println(Thread.currentThread().getName()+" 正在写操作"+key);
            //暂停一会
            TimeUnit.MICROSECONDS.sleep(300);
            //放数据
            map.put(key,value);
            System.out.println(Thread.currentThread().getName()+" 写完了"+key);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //释放写锁
            rwLock.writeLock().unlock();
        }
    }

    //取数据
    public Object get(String key) {
        //添加读锁
        rwLock.readLock().lock();
        Object result = null;
        try {
            System.out.println(Thread.currentThread().getName()+" 正在读取操作"+key);
            //暂停一会
            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();
        //创建线程放数据
        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);

        //创建线程取数据
        for (int i = 1; i <=5; i++) {
            final int num = i;
            new Thread(()->{
                myCache.get(num+"");
            },String.valueOf(i)).start();
        }
    }
}
登入後複製
###5. 互斥鎖##### #互斥鎖是獨佔鎖的一種常規實現,是指某一資源同時只允許一個訪問者對其進行訪問,具有唯一性和排它性###
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;//创建互斥锁并初始化

pthread_mutex_lock(&mutex);//对线程上锁,此时其他线程阻塞等待该线程释放锁

//要执行的代码段

pthread_mutex_unlock(&mutex);//执行完后释放锁
登入後複製
###6. 自旋鎖## ####查看百度百科的解釋,如下:###

它是为实现保护共享资源而提出一种锁机制。其实,自旋锁与互斥锁比较类似,它们都是为了解决对某项资源的互斥使用。无论是互斥锁,还是自旋锁,在任何时刻,最多只能有一个保持者,也就说,在任何时刻最多只能有一个执行单元获得锁。但是两者在调度机制上略有不同。对于互斥锁,如果资源已经被占用,资源申请者只能进入睡眠状态。但是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,"自旋"一词就是因此而得名

通俗的来说就是一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。获取锁的线程一直处于活跃状态,但是并没有执行任何有效的任务。

其特点:

  1. 持有锁时间等待过长,消耗CPU

  2. 无法满足等待时间最长的线程优先获取锁。不公平的锁就会存在“线程饥饿”问题

  3. 自旋锁不会使线程状态发生切换,处于用户态(不会到内核态进行线程的状态转换),一直都是活跃,不会使线程进入阻塞状态,减少了不必要的上下文切换,执行速度快。

其模拟算法如下

do{
	b=1;
	while(b){
		lock(bus);
		b = test_and_set(&lock);
		unlock(bus);
	}
	//临界区
	//lock = 0;
	//其余部分
}while(1)
登入後複製

7. 无锁 / 偏向锁 / 轻量级锁 / 重量级锁

  • 无锁:没有对资源进行锁定,所有的线程都能访问并修改同一个资源,但同时只有一个线程能修改成功

  • 偏向锁:是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁,降低获取锁的代价

  • 轻量级锁:锁是偏向锁的时候,被另外的线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,从而提高性能

  • 重量级锁:线程并发加剧,线程的自旋超过了一定次数,或者一个线程持有锁,一个线程在自旋,还有线程要访问

以上是java中各類鎖的機制是什麼的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:yisu.com
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板