qu'est-ce que la synchronisation des threads Java
Dec 09, 2019 pm 04:27 PMSynchronisation des threads
Lors de l'appel du même objet entre plusieurs threads, pour la sécurité et la précision du fonctionnement, l'objet doit être synchronisé pour garantir que le résultat de l'objet est correct lorsqu'il est utilisé par chaque thread, et que l'état de l'objet est raisonnablement , cette partie implique des points de connaissances tels que la synchronisation et les thread locks. Cette partie n'implique que les notions de verrouillage synchronisé et de synchronisation (Lock).
synchronized
Le mot-clé synchronisé peut modifier des objets et des méthodes. L'utilisation habituelle est la suivante?:
//同步代碼塊 synchronized(Object object){ ... } //或者 //同步方法 public synchronized void test(){ ... }
Il existe un concept de moniteur de synchronisation, comme ci-dessus L'objet objet du bloc de code synchronisé et l'objet this de la méthode synchronisée seront surveillés de manière synchrone. Lorsque plusieurs threads appellent un bloc de code ou une méthode synchronisé en même temps, un seul thread peut obtenir le verrou d'objet surveillé synchronisé. à tout moment.Après avoir exécuté le code, le verrou sera libéré plus tard. Pendant cette période, les autres threads appelants ne peuvent qu'attendre que le verrou soit libéré avant d'appeler.
La méthode sell de la classe SellRunnable mentionnée ci-dessus utilise également synchronisé. Le code ci-dessus s'exécute trop rapidement, il ne peut donc pas être détecté si vous le modifiez, vous pouvez comprendre la différence entre synchronisé et non.
public class ThreadTest { public static void main(String[] args) { SellRunnable sellRunnable = new SellRunnable(); Thread thread1 = new Thread(sellRunnable, "1"); Thread thread2 = new Thread(sellRunnable, "2"); Thread thread3 = new Thread(sellRunnable, "3"); thread2.start(); thread1.start(); thread3.start(); } } class SellRunnable implements Runnable { //有十張票 int index = 10; public void sell() { if (index >= 1) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } index--; System.out.println("售貨窗口:" + Thread.currentThread().getName() + " 賣出了一張票,剩余: " + index); } else { System.out.println("售貨窗口:" + Thread.currentThread().getName() + " 買票時沒票了"); } } @Override public void run() { while (index > 0) { System.out.println("售貨窗口:" + Thread.currentThread().getName() + " 開始買票"); sell(); } } } //執(zhí)行結(jié)果: 售貨窗口:1 開始買票 售貨窗口:2 開始買票 售貨窗口:3 開始買票 售貨窗口:2 賣出了一張票,剩余:9 售貨窗口:2 開始買票 售貨窗口:1 賣出了一張票,剩余:9 售貨窗口:1 開始買票 售貨窗口:3 賣出了一張票,剩余:8 售貨窗口:3 開始買票 售貨窗口:1 賣出了一張票,剩余:6 售貨窗口:1 開始買票 售貨窗口:2 賣出了一張票,剩余:6 售貨窗口:2 開始買票 售貨窗口:3 賣出了一張票,剩余:5 售貨窗口:3 開始買票 售貨窗口:1 賣出了一張票,剩余:4 售貨窗口:1 開始買票 售貨窗口:2 賣出了一張票,剩余:3 售貨窗口:3 賣出了一張票,剩余:2 售貨窗口:3 開始買票 售貨窗口:2 開始買票 售貨窗口:3 賣出了一張票,剩余:1 售貨窗口:2 賣出了一張票,剩余:0 售貨窗口:1 賣出了一張票,剩余:1 Process finished with exit code 0 //可以看到,票數(shù)減少是錯誤的 //sell方法添加synchronized修飾符后 執(zhí)行結(jié)果: public synchronized void sell() { if (index >= 1) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } index--; System.out.println("售貨窗口:" + Thread.currentThread().getName() + " 賣出了一張票,剩余: " + index); } else { System.out.println("售貨窗口:" + Thread.currentThread().getName() + " 買票時沒票了"); } } 售貨窗口:2 開始買票 售貨窗口:3 開始買票 售貨窗口:1 開始買票 售貨窗口:2 賣出了一張票,剩余:9 售貨窗口:2 開始買票 售貨窗口:1 賣出了一張票,剩余:8 售貨窗口:1 開始買票 售貨窗口:3 賣出了一張票,剩余:7 售貨窗口:3 開始買票 售貨窗口:1 賣出了一張票,剩余:6 售貨窗口:1 開始買票 售貨窗口:2 賣出了一張票,剩余:5 售貨窗口:2 開始買票 售貨窗口:1 賣出了一張票,剩余:4 售貨窗口:1 開始買票 售貨窗口:1 賣出了一張票,剩余:3 售貨窗口:1 開始買票 售貨窗口:3 賣出了一張票,剩余:2 售貨窗口:3 開始買票 售貨窗口:1 賣出了一張票,剩余:1 售貨窗口:1 開始買票 售貨窗口:1 賣出了一張票,剩余:0 售貨窗口:2 買票時沒票了 售貨窗口:3 買票時沒票了 Process finished with exit code 0 // 可以看到,票數(shù)是正常減少的
Après la synchronisation ci-dessus de la méthode de vente, à un certain moment, un seul thread appellera cette méthode, donc le résultat obtenu en jugeant l'index est le résultat correct.
Pendant la synchronisation ci-dessus, la sécurité des threads est assurée en réduisant l'efficacité de fonctionnement. Pour cette raison, ne synchronisez pas les méthodes et les objets inutiles dans les classes d'utilisation des threads, et synchronisez uniquement les ressources ou les objets avec concurrence.
Après l'identification de la synchronisation, les points suivants peuvent libérer le verrou?:
Bloc de code, exécution de la méthode terminée (achèvement normal, retour ou pause, exception levée)
Appeler l'attente La méthode est utilisée pour mettre en pause le thread en cours.
Lorsque le thread exécute un bloc de code synchronisé, les méthodes sleep et rendement ne libéreront pas le verrou de synchronisation, et la méthode suspend ne sera pas non plus suspendue (essayez d'éviter d'utiliser suspendre et reprendre pour manipuler l'état du thread pendant le fonctionnement du thread, ce qui mène facilement à une impasse. )
Verrouillage synchronisé
Le synchronisé mentionné ci-dessus est un mot-clé en Java, et il est également mentionné dans Lors de la mise en veille ou de l'exécution d'opérations d'E/S, le thread ne libère pas le verrou de thread et les autres threads doivent attendre. Cela réduit parfois l'efficacité de l'exécution, donc une alternative qui peut libérer le verrou de thread lorsque le thread est bloqué est apparue. juste pour résoudre ce problème.
Lock est une classe en java, dans le package java.util.concurrent.locks, le code spécifique est le suivant :
public interface Lock { void lock();//加鎖 void lockInterruptibly() throws InterruptedException;//加鎖 boolean tryLock();//加鎖 boolean tryLock(long time, TimeUnit unit) throws InterruptedException;//加鎖 void unlock();//釋放鎖 Condition newCondition();//線程協(xié)作中用到 }
Une sous-classe d'implémentation de l'interface Lock est ReentrantLock, en java . Sous le package util.concurrent.locks, le code source de ReentrantLock est le suivant?:
public class ReentrantLock implements Lock, Serializable { private static final long serialVersionUID = 7373984872572414699L; private final ReentrantLock.Sync sync; public ReentrantLock() { this.sync = new ReentrantLock.NonfairSync(); } public ReentrantLock(boolean var1) {//是否創(chuàng)建公平鎖 this.sync = (ReentrantLock.Sync)(var1?new ReentrantLock.FairSync():new ReentrantLock. NonfairSync()); } public void lock() { this.sync.lock(); } public void lockInterruptibly() throws InterruptedException { this.sync.acquireInterruptibly(1); } public boolean tryLock() { return this.sync.nonfairTryAcquire(1); } public boolean tryLock(long var1, TimeUnit var3) throws InterruptedException { return this.sync.tryAcquireNanos(1, var3.toNanos(var1)); } public void unlock() { this.sync.release(1); } public Condition newCondition() { return this.sync.newCondition(); } public int getHoldCount() {//當(dāng)前線程持有該鎖的數(shù)量 return this.sync.getHoldCount(); } public boolean isHeldByCurrentThread() {//該鎖是否被當(dāng)前線程持有 return this.sync.isHeldExclusively(); } public boolean isLocked() {//是否被其他線程持有該鎖 return this.sync.isLocked(); } public final boolean isFair() {//是否是公平鎖 return this.sync instanceof ReentrantLock.FairSync; } protected Thread getOwner() {//當(dāng)前鎖的持有線程 return this.sync.getOwner(); } public final boolean hasQueuedThreads() {//是否有線程在等待該鎖 return this.sync.hasQueuedThreads(); } public final boolean hasQueuedThread(Thread var1) {//目標(biāo)線程是否在等待該鎖 return this.sync.isQueued(var1); } public final int getQueueLength() {//等待該鎖線程的數(shù)量 return this.sync.getQueueLength(); } protected Collection<Thread> getQueuedThreads() {//獲取所有等待該鎖的線程集合 return this.sync.getQueuedThreads(); } ... }
Comment utiliser Lock
lock<. ??>
lock() est utilisé pour acquérir le verrou Si le verrou est occupé par d'autres threads, il attendra.public class LockTest { public static void main(String[] args) { com.test.java.SellRunnable sellRunnable = new com.test.java.SellRunnable(); Thread thread1 = new Thread(sellRunnable, "1號窗口"); Thread thread2 = new Thread(sellRunnable, "2號窗口"); Thread thread3 = new Thread(sellRunnable, "3號窗口"); thread1.start(); thread2.start(); thread3.start(); } }
public class SellRunnable implements Runnable { //有十張票 int index = 10; Lock lock = new ReentrantLock(); public void sell() { try { lock.lock(); System.out.println("售貨柜臺:" + Thread.currentThread().getName() + "獲取了票源+++++"); if (index >= 1) { index--; System.out.println("售貨柜臺:" + Thread.currentThread().getName() + "賣出了一張票,剩余: " + index); } else { System.out.println("售貨柜臺:" + Thread.currentThread().getName() + "買票時沒票了000"); } } finally { lock.unlock(); } } @Override public void run() { while (index > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } sell(); } } }Résultat de l'exécution?:
售貨柜臺:3號窗口獲取了票源+++++ 售貨柜臺:3號窗口賣出了一張票,剩余:9 售貨柜臺:1號窗口獲取了票源+++++ 售貨柜臺:1號窗口賣出了一張票,剩余:8 售貨柜臺:2號窗口獲取了票源+++++ 售貨柜臺:2號窗口賣出了一張票,剩余:7 售貨柜臺:1號窗口獲取了票源+++++ 售貨柜臺:1號窗口賣出了一張票,剩余:6 售貨柜臺:3號窗口獲取了票源+++++ 售貨柜臺:3號窗口賣出了一張票,剩余:5 售貨柜臺:2號窗口獲取了票源+++++ 售貨柜臺:2號窗口賣出了一張票,剩余:4 售貨柜臺:3號窗口獲取了票源+++++ 售貨柜臺:3號窗口賣出了一張票,剩余:3 售貨柜臺:1號窗口獲取了票源+++++ 售貨柜臺:1號窗口賣出了一張票,剩余:2 售貨柜臺:2號窗口獲取了票源+++++ 售貨柜臺:2號窗口賣出了一張票,剩余:1 售貨柜臺:3號窗口獲取了票源+++++ 售貨柜臺:3號窗口賣出了一張票,剩余:0 售貨柜臺:1號窗口獲取了票源+++++ 售貨柜臺:1號窗口買票時沒票了000 售貨柜臺:2號窗口獲取了票源+++++ 售貨柜臺:2號窗口買票時沒票了000 Process finished with exit code 0 //每一個窗口都隨機(jī)獲取票源、然后賣出票tryLocktryLock() tente d'acquérir le verrou si l'acquisition réussit, elle renvoie true. Si elle échoue, elle renvoie false. et n'entrera pas dans l'état d'attente.
public class SellRunnable implements Runnable { //有十張票 int index = 10; Lock lock = new ReentrantLock(); public void sell() { if (lock.tryLock()) { try { System.out.println("售貨柜臺:" + Thread.currentThread().getName() + "獲取了票源+++++"); if (index >= 1) { index--; System.out.println("售貨柜臺:" + Thread.currentThread().getName() + "賣出了一張票,剩余:" + index); } else { System.out.println("售貨柜臺:" + Thread.currentThread().getName() + "買票時沒票了000"); } } finally { lock.unlock(); } } else { System.out.println("售貨柜臺:" + Thread.currentThread().getName()+"沒有獲取票源?。?!"); } } @Override public void run() { while (index > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } sell(); } } }Résultats en cours d'exécution?:
售貨柜臺:1號窗口獲取了票源+++++ 售貨柜臺:3號窗口沒有獲取票源?。?! 售貨柜臺:2號窗口沒有獲取票源!?。?售貨柜臺:1號窗口賣出了一張票,剩余:9 售貨柜臺:2號窗口沒有獲取票源?。?! 售貨柜臺:3號窗口獲取了票源+++++ 售貨柜臺:3號窗口賣出了一張票,剩余:8 售貨柜臺:1號窗口獲取了票源+++++ 售貨柜臺:1號窗口賣出了一張票,剩余:7 售貨柜臺:1號窗口沒有獲取票源?。?! 售貨柜臺:3號窗口沒有獲取票源?。?! 售貨柜臺:2號窗口獲取了票源+++++ 售貨柜臺:2號窗口賣出了一張票,剩余:6 售貨柜臺:1號窗口獲取了票源+++++ 售貨柜臺:2號窗口沒有獲取票源!??! 售貨柜臺:3號窗口沒有獲取票源?。。?售貨柜臺:1號窗口賣出了一張票,剩余:5 售貨柜臺:2號窗口獲取了票源+++++ 售貨柜臺:1號窗口沒有獲取票源?。?! 售貨柜臺:2號窗口賣出了一張票,剩余:4 售貨柜臺:3號窗口沒有獲取票源?。?! 售貨柜臺:1號窗口獲取了票源+++++ 售貨柜臺:2號窗口沒有獲取票源?。?! 售貨柜臺:3號窗口沒有獲取票源?。?! 售貨柜臺:1號窗口賣出了一張票,剩余:3 售貨柜臺:1號窗口獲取了票源+++++ 售貨柜臺:1號窗口賣出了一張票,剩余:2 售貨柜臺:2號窗口獲取了票源+++++ 售貨柜臺:3號窗口沒有獲取票源?。。?售貨柜臺:2號窗口賣出了一張票,剩余:1 售貨柜臺:1號窗口獲取了票源+++++ 售貨柜臺:1號窗口賣出了一張票,剩余:0 售貨柜臺:3號窗口沒有獲取票源?。?! 售貨柜臺:2號窗口沒有獲取票源?。?! Process finished with exit code 0//沒有獲取到貨源的票口,就直接沒有等待,進(jìn)入下次買票tryLock(long time, TimeUnit unit)tryLock(long time, TimeUnit unit) peut être configuré pour attendre un certain temps lorsque le verrou ne peut pas être obtenu. //Le premier paramètre est long, l'unité de temps du deuxième paramètre est
public class SellRunnable implements Runnable { //有十張票 int index = 10; Lock lock = new ReentrantLock(); public void sell() { try { if (lock.tryLock(1000, TimeUnit.MILLISECONDS)) { try { System.out.println("售貨柜臺:" + Thread.currentThread().getName() + "獲取了票源+++++"); if (index >= 1) { index--; System.out.println("售貨柜臺:" + Thread.currentThread().getName() +"賣出了一張票,剩余:" + index); } else { System.out.println("售貨柜臺:" + Thread.currentThread(). getName() + "買票時沒票了000"); } try { Thread.sleep(2000);//人為加入買票時間 } catch (InterruptedException e) { e.printStackTrace(); } } finally { lock.unlock(); } } else { System.out.println("售貨柜臺:" + Thread.currentThread().getName() + "沒有獲取票源!??!"); } } catch (InterruptedException e) { e.printStackTrace(); } } @Override public void run() { while (index > 0) { try { Thread.sleep(500);//要不執(zhí)行太快,看不出效果 } catch (InterruptedException e) { e.printStackTrace(); } sell(); } } }Résultat de l'exécution :
售貨柜臺:1號窗口獲取了票源+++++ 售貨柜臺:1號窗口賣出了一張票,剩余:9 售貨柜臺:2號窗口沒有獲取票源!?。?售貨柜臺:3號窗口沒有獲取票源?。?! 售貨柜臺:2號窗口獲取了票源+++++ 售貨柜臺:2號窗口賣出了一張票,剩余:8 售貨柜臺:3號窗口沒有獲取票源?。?! 售貨柜臺:1號窗口沒有獲取票源!??! 售貨柜臺:3號窗口獲取了票源+++++ 售貨柜臺:3號窗口賣出了一張票,剩余:7 售貨柜臺:1號窗口沒有獲取票源?。?! 售貨柜臺:2號窗口沒有獲取票源!?。?售貨柜臺:1號窗口獲取了票源+++++ 售貨柜臺:1號窗口賣出了一張票,剩余:6 售貨柜臺:2號窗口沒有獲取票源?。?! 售貨柜臺:3號窗口沒有獲取票源?。。?售貨柜臺:2號窗口獲取了票源+++++ 售貨柜臺:2號窗口賣出了一張票,剩余:5 售貨柜臺:3號窗口沒有獲取票源?。。?售貨柜臺:1號窗口沒有獲取票源?。?! 售貨柜臺:3號窗口獲取了票源+++++ 售貨柜臺:3號窗口賣出了一張票,剩余:4 售貨柜臺:1號窗口沒有獲取票源!??! 售貨柜臺:2號窗口沒有獲取票源!??! 售貨柜臺:1號窗口獲取了票源+++++ 售貨柜臺:1號窗口賣出了一張票,剩余:3 售貨柜臺:2號窗口沒有獲取票源?。?! 售貨柜臺:3號窗口沒有獲取票源?。?! 售貨柜臺:2號窗口獲取了票源+++++ 售貨柜臺:2號窗口賣出了一張票,剩余:2 售貨柜臺:3號窗口沒有獲取票源!?。?售貨柜臺:1號窗口沒有獲取票源?。。?售貨柜臺:3號窗口獲取了票源+++++ 售貨柜臺:3號窗口賣出了一張票,剩余:1 售貨柜臺:1號窗口沒有獲取票源?。?! 售貨柜臺:2號窗口沒有獲取票源?。?! 售貨柜臺:1號窗口獲取了票源+++++ 售貨柜臺:1號窗口賣出了一張票,剩余:0 售貨柜臺:2號窗口沒有獲取票源!??! 售貨柜臺:3號窗口沒有獲取票源?。。?Process finished with exit code 0 //當(dāng)買票時間大約等待時間時,則沒有獲取票源的窗口不買票,進(jìn)入下個買票機(jī)會Raccourcir le délai d'achat du ticket :
try { Thread.sleep(500);//人為加入買票時間 } catch (InterruptedException e) { e.printStackTrace(); }Résultat de l'exécution :
售貨柜臺:1號窗口獲取了票源+++++ 售貨柜臺:1號窗口賣出了一張票,剩余:9 售貨柜臺:2號窗口獲取了票源+++++ 售貨柜臺:2號窗口賣出了一張票,剩余:8 售貨柜臺:3號窗口沒有獲取票源!?。?售貨柜臺:1號窗口獲取了票源+++++ 售貨柜臺:1號窗口賣出了一張票,剩余:7 售貨柜臺:2號窗口獲取了票源+++++ 售貨柜臺:2號窗口賣出了一張票,剩余:6 售貨柜臺:1號窗口獲取了票源+++++ 售貨柜臺:1號窗口賣出了一張票,剩余:5 售貨柜臺:3號窗口沒有獲取票源?。?! 售貨柜臺:2號窗口獲取了票源+++++ 售貨柜臺:2號窗口賣出了一張票,剩余:4 售貨柜臺:3號窗口獲取了票源+++++ 售貨柜臺:3號窗口賣出了一張票,剩余:3 售貨柜臺:1號窗口獲取了票源+++++ 售貨柜臺:1號窗口賣出了一張票,剩余:2 售貨柜臺:2號窗口獲取了票源+++++ 售貨柜臺:2號窗口賣出了一張票,剩余:1 售貨柜臺:3號窗口獲取了票源+++++ 售貨柜臺:3號窗口賣出了一張票,剩余:0 售貨柜臺:1號窗口獲取了票源+++++ 售貨柜臺:1號窗口買票時沒票了000 售貨柜臺:2號窗口獲取了票源+++++ 售貨柜臺:2號窗口買票時沒票了000 Process finished with exit code 0 //等待時間內(nèi)獲取到票源了,也就賣出票了lockInterruptablylockInterruptably() Lors de l'acquisition d'un verrou via cette méthode, si le verrou est détenu par un autre thread, il entrera dans un état d'attente, mais ce processus d'attente peut être interrompu en appelant Thread The La méthode d'interruption de l'objet peut interrompre l'attente. Lorsqu'elle est interrompue, une InterruptedException est levée, qui doit être interceptée ou déclarée pour être levée.
public class ThreadTest { public static void main(String[] args) { SellRunnable sellRunnable = new SellRunnable(); Thread thread1 = new Thread(sellRunnable, "1號窗口"); Thread thread2 = new Thread(sellRunnable, "2號窗口"); Thread thread3 = new Thread(sellRunnable, "3號窗口"); thread1.start(); try { Thread.sleep(500);//確保窗口1號先獲取鎖 } catch (InterruptedException e) { e.printStackTrace(); } thread2.start(); thread3.start(); try { Thread.sleep(2000);//等待兩秒后,打斷窗口2、3的等待 } catch (InterruptedException e) { e.printStackTrace(); } thread2.interrupt(); thread3.interrupt(); } } SellRunnable中等待時間加長: try { Thread.sleep(5000);//人為加入買票時間 } catch (InterruptedException e) { e.printStackTrace(); }Résultats de l'exécution?:
售貨柜臺:1號窗口獲取了票源+++++ 售貨柜臺:1號窗口賣出了一張票,剩余:9 售貨柜臺:3號窗口被打斷了 //這個地方被打斷了 售貨柜臺:2號窗口被打斷了 //這個地方被打斷了 售貨柜臺:2號窗口獲取了票源+++++ 售貨柜臺:2號窗口賣出了一張票,剩余:8 售貨柜臺:3號窗口獲取了票源+++++ 售貨柜臺:3號窗口賣出了一張票,剩余:7 售貨柜臺:1號窗口獲取了票源+++++ 售貨柜臺:1號窗口賣出了一張票,剩余:6 售貨柜臺:2號窗口獲取了票源+++++ 售貨柜臺:2號窗口賣出了一張票,剩余:5 售貨柜臺:3號窗口獲取了票源+++++ 售貨柜臺:3號窗口賣出了一張票,剩余:4 售貨柜臺:1號窗口獲取了票源+++++ 售貨柜臺:1號窗口賣出了一張票,剩余:3 售貨柜臺:2號窗口獲取了票源+++++ 售貨柜臺:2號窗口賣出了一張票,剩余:2 售貨柜臺:3號窗口獲取了票源+++++ 售貨柜臺:3號窗口賣出了一張票,剩余:1 售貨柜臺:1號窗口獲取了票源+++++ 售貨柜臺:1號窗口賣出了一張票,剩余:0 售貨柜臺:2號窗口獲取了票源+++++ 售貨柜臺:2號窗口買票時沒票了000 售貨柜臺:3號窗口獲取了票源+++++ 售貨柜臺:3號窗口買票時沒票了000 Process finished with exit code 0
Comparaison entre synchronisé et Lock
Grace au code ci-dessus, vous pouvez voir la différence entre Lock et synchronisé Plusieurs connexions et différences?: Les deux sont des verrous réentrants Le verrouillage réentrant signifie qu'après qu'un thread a acquis le verrou d'objet, le thread peut à nouveau acquérir le verrou d'objet Non bloqué. Par exemple, après que plusieurs méthodes (ou une méthode appelée de manière récursive) dans la même classe soient synchronisées ou verrouillées, le même thread peut acquérir le verrou de l'objet sans être bloqué lors de l'appel de ces deux méthodes. Exemple de verrou non réentrant?:public class Lock{ private boolean isLocked = false; public void lock(){ while(isLocked){ wait(); } isLocked = true; } public void unlock(){ isLocked = false; notify(); } } //使用方法: public class Test{ Lock lock = new Lock(); public void test1(){ lock.lock(); test2(); lock.unlock(); } public void test2(){ lock.lock(); ... lock.unlock(); } }Lorsque la classe Test appelle la méthode test1, après avoir exécuté lock.lock() et appelé test2, elle attendra indéfiniment et deviendra Lock mort. Principe de conception du verrouillage réentrant?:
public class Lock{ private boolean isLocked = false; private Thread lockedThread = null; int lockedCount = 0; public void lock(){ Thread thread = Thread.currentThread(); while(isLocked && thread != lockedThread){ wait(); } isLocked = true; lockedCount++; lockedThread = thread; } public void unlock(){ Thread thread = Thread.currentThread(); if(thread == lockedThread){ lockedCount--; if(lockedCount == 0){ isLocked = false; lockedThread = null; notify(); } } } }Après avoir appelé la méthode test1 de la classe Test de cette manière, la méthode test2 peut également être exécutée en douceur. La mise en ?uvre de la synchronisation utilise essentiellement des compteurs pour réaliser la réentrance. Le verrouillage est un verrouillage interrompu, la synchronisation ne peut pas être interrompue.
當(dāng)一個線程B執(zhí)行被鎖的對象的代碼時,發(fā)現(xiàn)線程A已經(jīng)持有該鎖,那么線程B就會進(jìn)入等待,但是synchronized就無法中斷該等待過程,而Lock就可以通過lockInterruptibly方法拋出異常從而中斷等待,去處理別的事情。
Lock可創(chuàng)建公平鎖,synchronized是非公平鎖。
公平鎖的意思是按照請求的順序來獲取鎖,不平公鎖就無法保證線程獲取鎖的先后次序。
Lock可以知道是否獲取到鎖,synchronized不可以。
synchronized在發(fā)生異?;蛘哌\行完畢,會自動釋放線程占有的鎖。而Lock需要主動釋放鎖,否則會鎖死;
synchronized在阻塞時,別的線程無法獲取鎖,Lock可以(這也是lock設(shè)計的一個目的)。
讀寫鎖
多個線程對同一個文件進(jìn)行寫操作時,會發(fā)生沖突所以需要加鎖,但是對同一個文件進(jìn)行讀操作的時候,使用上面的方法會造成效率的降低,所以基于這種情況,產(chǎn)生了ReadWriteLock這個接口:
public interface ReadWriteLock { /** * Returns the lock used for reading. * * @return the lock used for reading. */ Lock readLock();//讀的鎖 /** * Returns the lock used for writing. * * @return the lock used for writing. */ Lock writeLock();//寫的鎖 }
這個接口的實現(xiàn)類是ReentrantReadWriteLock,其源代碼如下:
public class ReentrantReadWriteLock implements ReadWriteLock, Serializable { private static final long serialVersionUID = -6992448646407690164L; private final ReentrantReadWriteLock.ReadLock readerLock; private final ReentrantReadWriteLock.WriteLock writerLock; ... public ReentrantReadWriteLock.WriteLock writeLock() {//獲取write lock return this.writerLock; } public ReentrantReadWriteLock.ReadLock readLock() {//獲取read lock return this.readerLock; } ... }
使用方法和Lock一樣,使用到write時調(diào)用writeLock()方法獲取lock進(jìn)行加鎖,使用到read時調(diào)用readLock()方法進(jìn)行加鎖,需要注意的知識點如下:
線程A占用寫鎖,線程B在申請寫、讀的時候需要等待。
線程A占用讀鎖,線程B在申請寫操作時,需要等待。
線程A占用讀鎖,線程B獲取讀操作時可以獲取到。
總結(jié)
如果需要效率提升,則建議使用Lock,如果效率要求不高,則synchronized滿足使用條件,業(yè)務(wù)邏輯寫起來也簡單,不需要手動釋放鎖。
PHP中文網(wǎng),有大量免費的JAVA入門教程,歡迎大家學(xué)習(xí)!
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Outils d'IA chauds

Undress AI Tool
Images de déshabillage gratuites

Undresser.AI Undress
Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover
Outil d'IA en ligne pour supprimer les vêtements des photos.

Clothoff.io
Dissolvant de vêtements AI

Video Face Swap
échangez les visages dans n'importe quelle vidéo sans effort grace à notre outil d'échange de visage AI entièrement gratuit?!

Article chaud

Outils chauds

Bloc-notes++7.3.1
éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise
Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1
Puissant environnement de développement intégré PHP

Dreamweaver CS6
Outils de développement Web visuel

SublimeText3 version Mac
Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Pour gérer correctement les transactions JDBC, vous devez d'abord désactiver le mode de validation automatique, puis effectuer plusieurs opérations, et enfin vous engager ou randonner en fonction des résultats; 1. Appelez Conn.SetAutoCommit (false) pour démarrer la transaction; 2. Exécuter plusieurs opérations SQL, telles que l'insertion et la mise à jour; 3. Appelez Conn.Commit () Si toutes les opérations sont réussies, et appelez Conn.Rollback () Si une exception se produit pour garantir la cohérence des données; Dans le même temps, les ressources TRY-With doivent être utilisées pour gérer les ressources, gérer correctement les exceptions et cl?turer les connexions pour éviter la fuite de connexion; De plus, il est recommandé d'utiliser des pools de connexion et de définir des points de sauvegarde pour réaliser un retour en arrière partiel, et de maintenir les transactions aussi courtes que possible pour améliorer les performances.

Utilisez des classes dans le package Java.Time pour remplacer les anciennes classes de date et de calendrier; 2. Obtenez la date et l'heure actuelles via LocalDate, LocalDateTime et Localtime; 3. Créez une date et une heure spécifiques en utilisant la méthode OF (); 4. Utilisez la méthode plus / moins pour augmenter et diminuer le temps; 5. Utilisez ZonedDateTime et ZoneID pour traiter le fuseau horaire; 6. Format et cha?nes de date d'analyse via DateTimeFormatter; 7. Utilisez instantanément pour être compatible avec les anciens types de dates si nécessaire; Le traitement des dattes dans le Java moderne devrait donner la priorité à l'utilisation de Java.timeapi, qui fournit clairement, immuable et linéaire

Pré-formancetartuptimemoryusage, quarkusandmicronautleadduetocompile-timeprocessingandgraalvsupport, withquarkusofperforming lightbetterine scénarios.

La collecte des ordures de Java (GC) est un mécanisme qui gère automatiquement la mémoire, ce qui réduit le risque de fuite de mémoire en récupérant des objets inaccessibles. 1. GC juge l'accessibilité de l'objet de l'objet racine (tel que les variables de pile, les threads actifs, les champs statiques, etc.), et les objets inaccessibles sont marqués comme des ordures. 2. Sur la base de l'algorithme de compensation de marque, marquez tous les objets accessibles et effacez des objets non marqués. 3. Adopter une stratégie de collecte générationnelle: la nouvelle génération (Eden, S0, S1) exécute fréquemment MinorGC; Les personnes agées fonctionnent moins, mais prend plus de temps pour effectuer MajorGC; Metaspace Stores Metadata de classe. 4. JVM fournit une variété de périphériques GC: SerialGC convient aux petites applications; Le parallelGC améliore le débit; CMS réduit

HTTP Log Middleware dans GO peut enregistrer les méthodes de demande, les chemins de requête, la propriété intellectuelle du client et le temps qui prend du temps. 1. Utilisez http.handlerfunc pour envelopper le processeur, 2. Enregistrez l'heure de début et l'heure de fin avant et après l'appel Suivant.Servehttp, 3. Obtenez le vrai client IP via R.RemoteAddr et X-Forwared-For Headers, 4. Utilisez le log.printf aux journaux de demande de sortie, 5. L'exemple de code complet a été vérifié pour s'exécuter et convient au démarrage d'un projet petit et moyen. Les suggestions d'extension incluent la capture des codes d'état, la prise en charge des journaux JSON et le suivi des ID de demande.

Le choix du bon type HTMLinput peut améliorer la précision des données, améliorer l'expérience utilisateur et améliorer la convivialité. 1. Sélectionnez les types d'entrée correspondants en fonction du type de données, tels que le texte, le courrier électronique, le tel, le numéro et la date, qui peuvent vérifier automatiquement la somme de la somme et l'adaptation au clavier; 2. Utilisez HTML5 pour ajouter de nouveaux types tels que l'URL, la couleur, la plage et la recherche, qui peuvent fournir une méthode d'interaction plus intuitive; 3. Utilisez l'espace réservé et les attributs requis pour améliorer l'efficacité et la précision du remplissage des formulaires, mais il convient de noter que l'espace réservé ne peut pas remplacer l'étiquette.

GradleisthebetterChoiceFormostNewProjectsDuetOtsSuperiorflexibility, Performance et ModerNtoolingSupport.1.gradle’sgroovy / kotlindslismoreConcis

Le report est utilisé pour effectuer des opérations spécifiées avant le retour de la fonction, telles que les ressources de nettoyage; Les paramètres sont évalués immédiatement lorsqu'ils sont reportés et les fonctions sont exécutées dans l'ordre de la dernière entrée (LIFO); 1. Plusieurs éleveurs sont exécutés dans l'ordre inverse des déclarations; 2. Communément utilisé pour le nettoyage sécurisé tel que la fermeture des fichiers; 3. La valeur de retour nommée peut être modifiée; 4. Il sera exécuté même si la panique se produit, adaptée à la récupération; 5. éviter l'abus de report dans les boucles pour éviter la fuite des ressources; Une utilisation correcte peut améliorer la sécurité et la lisibilité du code.
