一般的な Java ロックの概要
さまざまなロック メカニズムの区別とその使用方法
使用方法 | ロック名 |
---|---|
スレッドが同期リソースをロックしたいかどうかを確認します | 楽観的ロックと悲観的ロック |
同期リソースをロックした後、ブロックしますか? | #ブロックしない場合は、スピン ロックを使用できます|
再入可能ロック | ##複数のスレッドが共通のロックを共有します |
複数のスレッドがキューに入るために競合する必要がありますか? | |
1. 楽観的ロックと悲観的ロック悲観的ロック: 複数人で同時に実行することはできません。実行する場合は最初にロックしてください。このようなロック メカニズムの多くは、行ロック、テーブル ロック、読み取りロック、書き込みロックなど、操作前にすべてロックされる従来のリレーショナル データベースで使用されています。一貫性があるかどうかは、データにバージョンを追加し、データを同期的に更新し、バージョン番号を追加することです。ロックされず、バージョン番号を確認でき、人生におけるチケット取得と同様に複数人で操作できます。データを取得しに行くたびに他人が変更しないだろうと思うのでロックはかかりませんが、更新する際にはこの期間内に他人が更新したかどうかを判断し、バージョンなどの仕組みを利用することができます。数字。オプティミスティック ロックはマルチ読み取りアプリケーション タイプに適しており、スループットを向上させることができます。 Redis は、このチェック アンド セット メカニズムを使用してトランザクションを実装します。 (オプティミスティック ロックは、バージョン番号メカニズムと CAS アルゴリズムを使用して実装できます) 特定のCase は悲観的ロックと楽観的ロックを示しています 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 はデータベースをクリアします ただし、別のサーバーで exec を入力すると、エラーが表示されます 楽観的ロックのため、修正するとバージョンが変わります 一般: 悲観的ロック: 各人が単独で作業を完了すると、ロックとロック解除が実行されます。同時実行性の問題を解決するために、同時操作はサポートされていません。一度に 1 つずつしか操作できないため、非効率です。 オプティミスティック ロック: 何かが実行されるたびに、データのバージョン番号が比較されます。最初に提出した人が最初にバージョン番号を提出します 2. 公平なロックと不公平なロック 公平なロック: 早い者勝ち不公平なロック: 順番が違います、あなたキューに飛び込む可能性があります
ソース コードを参照してください パラメーターを指定した ReentrantLock(true) は公正なロックです ReentrantLock(false) は不公平なロックです 主に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(); } } ログイン後にコピー 結果のスクリーンショットは次のとおりです #スレッドは実行されますが、BC スレッドは実行されず、不当なロックが発生します その設定を具体的に変更するには、リエントラント ロックでパラメーター化されたコンストラクターを使用できます。コードをプライベート最終 ReentrantLock lock = new ReentrantLock(true) に変更します;コードのスクリーンショットは次のとおりです#3. リエントラント ロック リエントラント ロックは再帰的ロックとも呼ばれますリエントラント ロックでは、最初の亀裂が発生します。内部構造にずっと入ることができます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(); ログイン後にコピー 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. 読み取り/書き込みロック (共有ロックと排他ロック) を記述する必要があります。読み取りロックは共有ロックであり、書き込みロックは排他的ロックです共有ロックの特定の実装
//资源类 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(); } } } ログイン後にコピー pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;//创建互斥锁并初始化 pthread_mutex_lock(&mutex);//对线程上锁,此时其他线程阻塞等待该线程释放锁 //要执行的代码段 pthread_mutex_unlock(&mutex);//执行完后释放锁 ログイン後にコピー 它是为实现保护共享资源而提出一种锁机制。其实,自旋锁与互斥锁比较类似,它们都是为了解决对某项资源的互斥使用。无论是互斥锁,还是自旋锁,在任何时刻,最多只能有一个保持者,也就说,在任何时刻最多只能有一个执行单元获得锁。但是两者在调度机制上略有不同。对于互斥锁,如果资源已经被占用,资源申请者只能进入睡眠状态。但是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,"自旋"一词就是因此而得名 通俗的来说就是一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。获取锁的线程一直处于活跃状态,但是并没有执行任何有效的任务。 其特点:
其模拟算法如下 do{ b=1; while(b){ lock(bus); b = test_and_set(&lock); unlock(bus); } //临界区 //lock = 0; //其余部分 }while(1) ログイン後にコピー 7. 无锁 / 偏向锁 / 轻量级锁 / 重量级锁
|
以上がJavaのさまざまなロックのメカニズムは何ですかの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。