Ringkasan kunci Java biasa
Bezakan setiap mekanisme kunci dan cara menggunakannya
使用方法 | 锁名 |
---|---|
考察线程是否要锁住同步资源 | 乐观锁和悲观锁 |
锁住同步资源后,要不要阻塞 | 不阻塞可以使用自旋锁 |
一个线程多个流程获取同一把锁 | 可重入锁 |
多个线程公用一把锁 | 读写锁(写的共享锁) |
多个线程竞争要不要排队 | 公平锁与非公平锁 |
Kunci pesimis: Berbilang orang tidak boleh dilaksanakan pada masa yang sama terlebih dahulu apabila melaksanakan. Banyak mekanisme penguncian sedemikian digunakan dalam pangkalan data hubungan tradisional, seperti kunci baris, kunci meja, kunci baca, kunci tulis, dll., yang semuanya dikunci sebelum operasi
Penguncian optimistik: melalui versi Sama ada nombornya konsisten atau tidak ialah menambah versi pada data, mengemas kini data secara serentak dan menambah nombor versi. Ia tidak akan dikunci, nombor versi boleh ditentukan, dan boleh dikendalikan oleh berbilang orang, sama seperti merebut tiket dalam kehidupan. Setiap kali anda pergi untuk mendapatkan data, anda berfikir bahawa orang lain tidak akan mengubah suainya, jadi ia tidak akan dikunci Walau bagaimanapun, apabila mengemas kini, anda akan menilai sama ada orang lain telah mengemas kini data dalam tempoh ini nombor. Penguncian optimis sesuai untuk jenis aplikasi berbilang baca, yang boleh meningkatkan daya pemprosesan. Redis menggunakan mekanisme semak dan tetapkan ini untuk melaksanakan transaksi
(penguncian optimistik boleh dilaksanakan menggunakan mekanisme nombor versi dan algoritma CAS)
Melalui khusus Kes menunjukkan penguncian pesimis dan penguncian optimistik
Dalam rangka kerja redis
sebelum melaksanakan berbilang, laksanakan jam arahan
Format khusus adalah seperti berikut
watch key1 [key2]
Kod khusus Formatnya adalah seperti berikut
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 mengosongkan pangkalan data
Tetapi jika anda memasukkan exec pada pelayan lain, ralat akan dipaparkan
Oleh kerana penguncian optimistik digunakan, versi akan berubah selepas diubah suai
Secara umum:
Penguncian pesimis: Apabila setiap orang menyelesaikan tugasan sahaja, kunci dan buka kunci dilaksanakan. Selesaikan masalah konkurensi, tidak menyokong operasi serentak, hanya boleh beroperasi satu demi satu, kecekapan rendah
Kunci optimis: setiap kali sesuatu dilaksanakan, nombor versi data akan dibandingkan, sesiapa yang menyerahkan dahulu akan menyerahkan nombor versi dahulu
Kunci adil: dahulukan dahulu
Kunci tidak adil: tidak teratur, baris gilir boleh dilonjak
Kunci adil: kecekapan yang agak rendah
Kunci tidak adil: kecekapan tinggi, tetapi benang terdedah kepada kelaparan
Kunci melalui kunci fungsi ini = new ReentrantLock(true);. Buat kunci reentrant, benar bermakna kunci adil, palsu bermakna kunci tidak adil. Kunci lalai tidak adil
Dengan melihat kod sumber
ReentrantLock(true) dengan parameter ialah kunci adil
ReentrantLock(palsu) ialah kunci tidak adil
Terutamanya panggil NonfairSync() dan 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(); }
Nyatakan kod sumber kunci tidak adil dan kunci saksama
Lihat kod sumber kunci saksama
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; }
Pass Operasi khusus contoh kod
//第一步 创建资源类,定义属性和和操作方法 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(); } }
Tangkapan skrin hasil adalah seperti berikut
semuanya dilaksanakan oleh urutan A, tetapi utas BC tidak dilaksanakan, dan kunci tidak adil berlaku
Anda boleh menukar tetapannya secara khusus melalui pembina berparameter dalam kunci reentrant
Ubah suai kod kepada kunci ReentrantLock terakhir peribadi = ReentrantLock baharu. (benar);
Kod Tangkapan skrin ialah
Kunci masuk semula juga dipanggil kunci rekursif
<.> dan terdapat kunci reentrant Selepas itu, selepas memecahkan yang pertama, anda boleh memasuki struktur dalamanObject 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();disegerakkan (o) bermakna mengunci blok kod dalam semasa { }Di atas adalah mekanisme kunci yang disegerakkan Mekanisme kunci diterangkan di bawah
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(); } }Untuk kunci bersarang dalam kunci yang sama, kunci bersarang dalaman masih boleh dikeluarkan jika ia tidak dibuka kunci , tetapi jika anda melompat keluar dari utas, utas lain akan dilaksanakan Ia akan menyebabkan kebuntuan Untuk memahami konsep mengunci dan membuka kunci, anda mesti menulis
4. Kunci baca-tulis (kunci kongsi dan kunci eksklusif )
Kunci baca-tulis ReentrantReadWriteLock
Kunci baca ialah ReentrantReadWriteLock.ReadLock, readLock() kaedah
Kunci tulis ialah ReentrantReadWriteLock.WriteLock, dan kaedah writeLock()
mencipta objek kunci baca-tulis peribadi ReadWriteLock rwLock = new ReentrantReadWriteLock();
tulis kunci rwLock.writeLock().lock( );, membuka kunci ialah rwLock.writeLock().unlock();
Baca kunci dan kunci rwLock.readLock().lock();, buka kunci ialah rwLock.readLock ().unlock();
Analisis kes:
Simulasikan berbilang benang untuk mengambil dan membaca data dalam peta
Kod lengkap adalah seperti berikut
5. Mutex lock//资源类 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(); } } }
Mutex lock ialah pelaksanaan konvensional kunci eksklusif Ini bermakna bahawa sumber tertentu hanya membenarkan satu pelawat mengaksesnya pada masa yang sama
6. Self Twist lockpthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;//创建互斥锁并初始化 pthread_mutex_lock(&mutex);//对线程上锁,此时其他线程阻塞等待该线程释放锁 //要执行的代码段 pthread_mutex_unlock(&mutex);//执行完后释放锁
它是为实现保护共享资源而提出一种锁机制。其实,自旋锁与互斥锁比较类似,它们都是为了解决对某项资源的互斥使用。无论是互斥锁,还是自旋锁,在任何时刻,最多只能有一个保持者,也就说,在任何时刻最多只能有一个执行单元获得锁。但是两者在调度机制上略有不同。对于互斥锁,如果资源已经被占用,资源申请者只能进入睡眠状态。但是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,"自旋"一词就是因此而得名
通俗的来说就是一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。获取锁的线程一直处于活跃状态,但是并没有执行任何有效的任务。
其特点:
持有锁时间等待过长,消耗CPU
无法满足等待时间最长的线程优先获取锁。不公平的锁就会存在“线程饥饿”问题
自旋锁不会使线程状态发生切换,处于用户态(不会到内核态进行线程的状态转换),一直都是活跃,不会使线程进入阻塞状态,减少了不必要的上下文切换,执行速度快。
其模拟算法如下
do{ b=1; while(b){ lock(bus); b = test_and_set(&lock); unlock(bus); } //临界区 //lock = 0; //其余部分 }while(1)
无锁:没有对资源进行锁定,所有的线程都能访问并修改同一个资源,但同时只有一个线程能修改成功
偏向锁:是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁,降低获取锁的代价
轻量级锁:锁是偏向锁的时候,被另外的线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,从而提高性能
重量级锁:线程并发加剧,线程的自旋超过了一定次数,或者一个线程持有锁,一个线程在自旋,还有线程要访问
Atas ialah kandungan terperinci Apakah mekanisme pelbagai kunci dalam java. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!