Kilitlenme , iş parçacıkları (veya platformunuz yürütme birimlerini ne çağırırsa çağırsın) kaynakları aldığında meydana gelir, burada her kaynak bir seferde yalnızca bir iş parçacığı tarafından tutulabilir ve bu kaynakları ayırma önlenemeyecek şekilde tutarsa ve iş parçacıkları arasında bazı "dairesel" ilişki vardır, öyle ki kilitlenmedeki her iş parçacığı başka bir iş parçacığı tarafından tutulan bir miktar kaynağı elde etmeyi beklemektedir.
Öyleyse, kilitlenmeyi önlemenin kolay bir yolu, kaynaklara tam bir sıralama vermek ve kaynakların yalnızca sırayla iş parçacıkları tarafından elde edileceğine dair bir kural koymaktır . Tersine, kaynakları toplayan ancak bunları sırayla almayan iş parçacıkları çalıştırılarak kasıtlı olarak bir kilitlenme oluşturulabilir. Örneğin:
İki iplik, iki kilit. Birinci iş parçacığı, kilitleri belirli bir sırayla edinmeye çalışan bir döngü çalıştırır, ikinci iş parçacığı kilitleri ters sırada almaya çalışan bir döngü çalıştırır. Her iş parçacığı, kilitleri başarıyla aldıktan sonra her iki kilidi de serbest bırakır.
public class HighlyLikelyDeadlock {
static class Locker implements Runnable {
private Object first, second;
Locker(Object first, Object second) {
this.first = first;
this.second = second;
}
@Override
public void run() {
while (true) {
synchronized (first) {
synchronized (second) {
System.out.println(Thread.currentThread().getName());
}
}
}
}
}
public static void main(final String... args) {
Object lock1 = new Object(), lock2 = new Object();
new Thread(new Locker(lock1, lock2), "Thread 1").start();
new Thread(new Locker(lock2, lock1), "Thread 2").start();
}
}
Şimdi, bu soruya bir kaç yorum yaşandığını arasındaki fark dışarı noktası olasılığı ve kesinlik çıkmazdan. Bir anlamda, ayrım akademik bir konudur. Pratik açıdan, yukarıda yazdığım kodla kilitlenmeyen çalışan bir sistem görmek isterim :)
Bununla birlikte, mülakat soruları bazen akademik olabilir ve bu GK sorusunun başlıkta "kesinlikle" kelimesi vardır, bu nedenle aşağıdaki, kesinlikle çıkmaza giren bir programdır . İki Locker
nesne oluşturulur, her birine iki kilit verilir ve bir CountDownLatch
iş parçacığı arasında senkronizasyon için kullanılır. Her biri Locker
ilk kilidi kilitler ve ardından mandalı bir kez geri sayar. Her iki diş de bir kilit kazandığında ve mandalı geri saydığında, mandal bariyerini geçer ve ikinci bir kilit elde etmeye çalışır, ancak her durumda diğer iplik zaten istenen kilidi tutar. Bu durum, belirli bir çıkmaza neden olur.
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class CertainDeadlock {
static class Locker implements Runnable {
private CountDownLatch latch;
private Lock first, second;
Locker(CountDownLatch latch, Lock first, Lock second) {
this.latch = latch;
this.first = first;
this.second = second;
}
@Override
public void run() {
String threadName = Thread.currentThread().getName();
try {
first.lock();
latch.countDown();
System.out.println(threadName + ": locked first lock");
latch.await();
System.out.println(threadName + ": attempting to lock second lock");
second.lock();
System.out.println(threadName + ": never reached");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
public static void main(final String... args) {
CountDownLatch latch = new CountDownLatch(2);
Lock lock1 = new ReentrantLock(), lock2 = new ReentrantLock();
new Thread(new Locker(latch, lock1, lock2), "Thread 1").start();
new Thread(new Locker(latch, lock2, lock1), "Thread 2").start();
}
}