Thread-Synchronisierung
Wenn Sie dasselbe Objekt zwischen mehreren Threads aufrufen, muss das Objekt aus Sicherheits- und Genauigkeitsgründen synchronisiert werden, um sicherzustellen, dass das Ergebnis des Objekts bei Verwendung durch jeden Thread korrekt ist und der Status des Objekts angemessen ist Dieser Teil umfasst Wissenspunkte wie Synchronisation und Thread-Sperre. In diesem Teil geht es nur um die Konzepte Synchronisation und Synchronisationssperre (Sperre).
synchronized
Das synchronisierte Schlüsselwort kann Objekte und Methoden ?ndern. Die übliche Verwendung ist wie folgt:
//同步代碼塊 synchronized(Object object){ ... } //或者 //同步方法 public synchronized void test(){ ... }
Es gibt ein Konzept des Synchronisationsmonitors. wie oben: Das Objektobjekt des synchronisierten Codeblocks und dieses Objekt der synchronisierten Methode werden synchron überwacht. Wenn mehrere Threads gleichzeitig einen synchronisierten Codeblock oder eine synchronisierte Methode aufrufen, kann nur ein Thread die synchron überwachte Objektsperre erhalten Zu jedem Zeitpunkt nach der Ausführung des Codes Die Sperre wird sp?ter aufgehoben. W?hrend dieser Zeit k?nnen andere aufrufende Threads nur warten, bis die Sperre aufgehoben wird.
Die oben erw?hnte Verkaufsmethode in der SellRunnable-Klasse verwendet auch synchronisiert. Der obige Code wird zu schnell ausgeführt, sodass er nicht erkannt werden kann. Wenn Sie ihn ?ndern, k?nnen Sie den Unterschied verstehen, ob synchronisiert ist oder nicht.
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í)行結果: 售貨窗口: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í)行結果: 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ù)是正常減少的
Nach der obigen Synchronisierung der Verkaufsmethode ruft zu einem bestimmten Zeitpunkt nur ein Thread diese Methode auf, sodass das bei der Indexbeurteilung erhaltene Ergebnis das richtige Ergebnis ist.
W?hrend der oben genannten Synchronisierung wird die Thread-Sicherheit durch Reduzierung der Betriebseffizienz gew?hrleistet. Synchronisieren Sie daher keine unn?tigen Methoden und Objekte in Thread-Nutzungsklassen und synchronisieren Sie nur Ressourcen oder Objekte mit dem Code.
Nach der Synchronisationsidentifikation k?nnen die folgenden Punkte die Sperre aufheben:
Codeblock, Methodenausführung abgeschlossen (normaler Abschluss, Rückgabe oder Unterbrechung, ausgel?ste Ausnahme)
Aufruf Die Wartezeit -Methode wird verwendet, um den aktuellen Thread anzuhalten.
Wenn der Thread einen synchronisierten Codeblock ausführt, geben die Sleep- und Yield-Methoden weder die Synchronisationssperre auf noch wird die Suspend-Methode suspendiert (versuchen Sie zu vermeiden, den Thread-Status w?hrend des Thread-Betriebs mit Suspend und Resume zu manipulieren). was leicht zu einem Deadlock führt. )
Synchronisierte Sperre
Die oben erw?hnte Synchronisierung ist ein Schlüsselwort in Java und wird auch in erw?hnt Beim Schlafen oder Ausführen von E/A-Vorg?ngen gibt der Thread die Thread-Sperre nicht frei, und andere Threads müssen warten. Dies verringert manchmal die Ausführungseffizienz, sodass eine Alternative erforderlich ist, die die Thread-Sperre aufheben kann, wenn der Thread blockiert ist nur um dieses Problem zu l?sen.
Lock ist eine Klasse in Java. Im Paket java.util.concurrent.locks lautet der spezifische Code wie folgt:
public interface Lock { void lock();//加鎖 void lockInterruptibly() throws InterruptedException;//加鎖 boolean tryLock();//加鎖 boolean tryLock(long time, TimeUnit unit) throws InterruptedException;//加鎖 void unlock();//釋放鎖 Condition newCondition();//線程協(xié)作中用到 }
Eine Implementierungsunterklasse der Lock-Schnittstelle ist ReentrantLock in Java . Unter dem Paket util.concurrent.locks lautet der Quellcode von ReentrantLock wie folgt:
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() {//當前線程持有該鎖的數(shù)量 return this.sync.getHoldCount(); } public boolean isHeldByCurrentThread() {//該鎖是否被當前線程持有 return this.sync.isHeldExclusively(); } public boolean isLocked() {//是否被其他線程持有該鎖 return this.sync.isLocked(); } public final boolean isFair() {//是否是公平鎖 return this.sync instanceof ReentrantLock.FairSync; } protected Thread getOwner() {//當前鎖的持有線程 return this.sync.getOwner(); } public final boolean hasQueuedThreads() {//是否有線程在等待該鎖 return this.sync.hasQueuedThreads(); } public final boolean hasQueuedThread(Thread var1) {//目標線程是否在等待該鎖 return this.sync.isQueued(var1); } public final int getQueueLength() {//等待該鎖線程的數(shù)量 return this.sync.getQueueLength(); } protected Collection<Thread> getQueuedThreads() {//獲取所有等待該鎖的線程集合 return this.sync.getQueuedThreads(); } ... }
So verwenden Sie Lock
lock
lock() wird verwendet, um die Sperre zu erhalten. Wenn die Sperre von anderen Threads belegt ist, wird sie warten.
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(); } } }
Laufergebnis:
售貨柜臺: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 //每一個窗口都隨機獲取票源、然后賣出票
tryLock
tryLock() versucht, die Sperre zu erhalten, wenn sie fehlschl?gt, gibt sie false zurück und wird nicht in den Wartezustand wechseln.
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(); } } }
Laufende Ergebnisse:
售貨柜臺: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//沒有獲取到貨源的票口,就直接沒有等待,進入下次買票
tryLock(long time, TimeUnit-Einheit)
tryLock(long time, TimeUnit-Einheit) kann so eingestellt werden, dass eine bestimmte Zeitspanne gewartet wird Die Sperre kann nicht erhalten werden. //Der erste Parameter ist lang, die Zeiteinheit des zweiten Parameters ist
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(); } } }
Ausführungsergebnis:
售貨柜臺: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 //當買票時間大約等待時間時,則沒有獲取票源的窗口不買票,進入下個買票機會
Ticketkaufzeit verkürzen:
try { Thread.sleep(500);//人為加入買票時間 } catch (InterruptedException e) { e.printStackTrace(); }
Ausführungsergebnis:
售貨柜臺: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 //等待時間內獲取到票源了,也就賣出票了
lockInterruptably
lockInterruptably() Wenn Sie eine Sperre über diese Methode erhalten und die Sperre von einem anderen Thread gehalten wird, wechselt sie in einen Wartezustand. Dieser Wartevorgang kann jedoch durch Aufrufen von Thread The unterbrochen werden Die Interrupt-Methode des Objekts kann das Warten unterbrechen. Bei einer Unterbrechung wird eine InterruptedException ausgel?st, die abgefangen oder zum Ausl?sen deklariert werden muss.
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(); }
Ausführungsergebnisse:
售貨柜臺: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
Vergleich zwischen synchronisiert und gesperrt
Durch den obigen Code k?nnen Sie den Unterschied erkennen Zwischen Lock und Synchronized gibt es mehrere Verbindungen und Unterschiede:
Beide sind Wiedereintrittssperren
Wiedereintrittssperre bedeutet, dass der Thread die Objektsperre erneut erwerben kann, nachdem er sie erhalten hat. Nachdem beispielsweise mehrere Methoden (oder eine rekursiv aufgerufene Methode) in derselben Klasse synchronisiert oder gesperrt wurden, kann derselbe Thread die Sperre des Objekts erhalten, ohne beim Aufrufen dieser beiden Methoden blockiert zu werden.
Beispiel für eine nicht wiedereintrittsf?hige Sperre:
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(); } }
Wenn die Testklasse nach der Ausführung von lock.lock() und dem Aufruf von test2 die Methode test1 aufruft, wartet sie ewig und wird zur Sperre.
Reentrant Lock Design-Prinzip:
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(); } } } }
Nachdem die test1-Methode der Testklasse auf diese Weise aufgerufen wurde, kann auch die test2-Methode reibungslos ausgeführt werden.
Die Implementierung von synchronisiert verwendet grunds?tzlich Z?hler, um einen Wiedereintritt zu erreichen.
Sperre ist eine unterbrechbare Sperre, synchronisiert kann nicht unterbrochen werden.
當一個線程B執(zhí)行被鎖的對象的代碼時,發(fā)現(xiàn)線程A已經持有該鎖,那么線程B就會進入等待,但是synchronized就無法中斷該等待過程,而Lock就可以通過lockInterruptibly方法拋出異常從而中斷等待,去處理別的事情。
Lock可創(chuàng)建公平鎖,synchronized是非公平鎖。
公平鎖的意思是按照請求的順序來獲取鎖,不平公鎖就無法保證線程獲取鎖的先后次序。
Lock可以知道是否獲取到鎖,synchronized不可以。
synchronized在發(fā)生異常或者運行完畢,會自動釋放線程占有的鎖。而Lock需要主動釋放鎖,否則會鎖死;
synchronized在阻塞時,別的線程無法獲取鎖,Lock可以(這也是lock設計的一個目的)。
讀寫鎖
多個線程對同一個文件進行寫操作時,會發(fā)生沖突所以需要加鎖,但是對同一個文件進行讀操作的時候,使用上面的方法會造成效率的降低,所以基于這種情況,產生了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時調用writeLock()方法獲取lock進行加鎖,使用到read時調用readLock()方法進行加鎖,需要注意的知識點如下:
線程A占用寫鎖,線程B在申請寫、讀的時候需要等待。
線程A占用讀鎖,線程B在申請寫操作時,需要等待。
線程A占用讀鎖,線程B獲取讀操作時可以獲取到。
總結
如果需要效率提升,則建議使用Lock,如果效率要求不高,則synchronized滿足使用條件,業(yè)務邏輯寫起來也簡單,不需要手動釋放鎖。
PHP中文網,有大量免費的JAVA入門教程,歡迎大家學習!
Das obige ist der detaillierte Inhalt vonWas ist Java-Thread-Synchronisation?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Hei?e KI -Werkzeuge

Undress AI Tool
Ausziehbilder kostenlos

Undresser.AI Undress
KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover
Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Clothoff.io
KI-Kleiderentferner

Video Face Swap
Tauschen Sie Gesichter in jedem Video mühelos mit unserem v?llig kostenlosen KI-Gesichtstausch-Tool aus!

Hei?er Artikel

Hei?e Werkzeuge

Notepad++7.3.1
Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version
Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1
Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6
Visuelle Webentwicklungstools

SublimeText3 Mac-Version
Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

Um JDBC -Transaktionen korrekt zu verarbeiten, müssen Sie zun?chst den automatischen Komiti -Modus ausschalten und dann mehrere Vorg?nge ausführen und schlie?lich entsprechend den Ergebnissen festlegen oder rollen. 1. Nennen Sie Conn.SetAutoCommit (False), um die Transaktion zu starten. 2. Führen Sie mehrere SQL -Operationen aus, z. B. einfügen und aktualisieren. 3. Rufen Sie Conn.Commit () an, wenn alle Vorg?nge erfolgreich sind, und rufen Sie Conn.Rollback () auf, wenn eine Ausnahme auftritt, um die Datenkonsistenz zu gew?hrleisten. Gleichzeitig sollten Try-with-Ressourcen verwendet werden, um Ressourcen zu verwalten, Ausnahmen ordnungsgem?? zu behandeln und Verbindungen zu schlie?en, um Verbindungsleckage zu vermeiden. Darüber hinaus wird empfohlen, Verbindungspools zu verwenden und Save -Punkte zu setzen, um teilweise Rollback zu erreichen und Transaktionen so kurz wie m?glich zu halten, um die Leistung zu verbessern.

Verwenden Sie Klassen im Java.Time -Paket, um das alte Datum und die Kalenderklassen zu ersetzen. 2. Erhalten Sie das aktuelle Datum und die aktuelle Uhrzeit durch LocalDate, LocalDatetime und Local Time; 3. Erstellen Sie ein bestimmtes Datum und eine bestimmte Uhrzeit mit der von () Methode; 4.. Verwenden Sie die Plus/Minus -Methode, um die Zeit nicht zu erh?hen und zu verkürzen. 5. Verwenden Sie ZonedDatetime und zoneId, um die Zeitzone zu verarbeiten. 6. Format und analysieren Sie Datumszeichenfolgen über DateTimeFormatter; 7. Verwenden Sie sofortige, um bei Bedarf mit den alten Datumstypen kompatibel zu sein. Die Verarbeitung der Datum in der modernen Java sollte der Verwendung von Java.Timeapi vorrangig machen, was klare, unver?nderliche und linear ist

Pre-Formancetartuptimemoryusage, QuarkusandmicronautleadduToCompile-Time-foringandgraalvSupport, WithQuarkusofttenperformLightBetterin serverloser Szenarien.2. Thyvelopecosystem,

Networkportsandfirewallsworktogethertoenablecommunicationwhileensuringsecurity.1.Networkportsarevirtualendpointsnumbered0–65535,withwell-knownportslike80(HTTP),443(HTTPS),22(SSH),and25(SMTP)identifyingspecificservices.2.PortsoperateoverTCP(reliable,c

Die Müllsammlung von Java (GC) ist ein Mechanismus, der automatisch den Speicher verwaltet, der das Risiko eines Speicherlecks verringert, indem unerreichbare Objekte zurückgeführt werden. 1.GC beurteilt die Zug?nglichkeit des Objekts aus dem Stammobjekt (z. B. Stapelvariablen, aktive Threads, statische Felder usw.) und nicht erreichbare Objekte als Müll markiert. 2. Basierend auf dem markierten Algorithmus markieren Sie alle erreichbaren Objekte und l?schen Sie nicht markierte Objekte. 3.. Verfolgen Sie eine Generationskollektionsstrategie: Die neue Generation (Eden, S0, S1) führt h?ufig MollGC aus; Die ?lteren Menschen erzielen weniger, dauert jedoch l?nger, um MajorGC durchzuführen. MetaPace speichert Klassenmetadaten. 4. JVM bietet eine Vielzahl von GC -Ger?ten: SerialGC ist für kleine Anwendungen geeignet; ParallelgC verbessert den Durchsatz; CMS reduziert sich

Durch die Auswahl des richtigen HTMlinput -Typs kann die Datengenauigkeit verbessert, die Benutzererfahrung verbessert und die Benutzerfreundlichkeit verbessert werden. 1. W?hlen Sie die entsprechenden Eingabetypen gem?? dem Datentyp aus, z. B. Text, E -Mail, Tel, Nummer und Datum, die automatisch überprüft und an die Tastatur anpassen k?nnen. 2. Verwenden Sie HTML5, um neue Typen wie URL, Farbe, Reichweite und Suche hinzuzufügen, die eine intuitivere Interaktionsmethode bieten k?nnen. 3.. Verwenden Sie Platzhalter und erforderliche Attribute, um die Effizienz und Genauigkeit der Formulierung zu verbessern. Es sollte jedoch beachtet werden, dass der Platzhalter das Etikett nicht ersetzen kann.

GradleStheBetterChoiceFormostnewProjectsDuetoitSuperiorFlexibilit?t, Leistung und ModerntoolingSupport.1.GRADLE'SGROOVY/KOTLINDSLISMORECONCISEANDEIPRESSIVETHANMANMANBOSEXML.2.GRAGRECONCISEANDEPRPRESSIVETHANMAVENSVOSEXML.2.

Auf Defer wird verwendet, um bestimmte Vorg?nge auszuführen, bevor die Funktion zurückgibt, z. B. die Reinigungsressourcen. Die Parameter werden sofort bewertet, wenn sie aufgeschoben werden, und die Funktionen werden in der Reihenfolge von Last-In-First-Out (LIFO) ausgeführt. 1. Mehrere Defers werden in umgekehrter Reihenfolge der Erkl?rungen ausgeführt. 2. h?ufig für die sichere Reinigung wie das Schlie?en von Dateien verwendet; 3. Der benannte Rückgabewert kann ge?ndert werden; V. 5. Vermeiden Sie den Missbrauch von Verschiebungen in Schleifen, um Ressourcenleckage zu verhindern. Die korrekte Verwendung kann die Sicherheit und die Lesbarkeit der Code verbessern.
