Java’da Multithreading – Bölüm 9: Yeniden Girilir Kilitler (Re-entrant Locks)

Bu bölümde “synchronized” anahtar kelimesinin işlevine alternatif bir yöntem olan yeniden girilir kilitleri (re-entrant locks) inceleyeceğiz.

Yeniden girilir kilitlerin çalışma mantığı şu şekildedir: java.util.concurrent.locks.* paketi altındaki “ReentrantLock” sınıfı türünde yeniden girilir bir kilit oluşturulur. Aynı anda yalnızca bir threadin girmesini istediğimiz kod bloğu yeniden girilir kilidin “lock()” ve “unlock()” metotlarıyla çevrelenir.

Şimdi örnek programımız üzerinden incelememize devam edelim:

public class Runner {

    private int count = 0;

    private Lock lock = new ReentrantLock();

    private void increment() {
        for (int i = 0; i < 10000; i++) {
            count++;
        }
    }

    public void firstThread() {
        lock.lock();
        increment();
        lock.unlock();
    }

    public void secondThread() {
        lock.lock();
        increment();
        lock.unlock();
    }

    public void printCount() {
        System.out.println("Sayaç: " + count);
    }

}

Okumaya devam et

Java’da Multithreading – Bölüm 4: “synchronized” Kod Blokları

Önceki bölümde bir metodu aynı anda birden fazla threadin işletememesini istediğimizde “synchronized” anahtar kelimesini nasıl kullanabileceğimizi örneklemiştik. Bu bölümde ise “synchronized” anahtar kelimesinin imdadımıza yetiştiği bir başka durum üzerinde duracağız. Yine bir örnek ile açılışı yaparak derman arayacağımız derdi ortaya koyalım. Özet olarak iki tam sayı (integer) listemiz var ve bu listeleri rastgele sayılarla doldurmak istiyoruz:

public class Application {

    private List<Integer> list1 = new ArrayList<>();

    private List<Integer> list2 = new ArrayList<>();

    public static void main(String[] args) {
        Application app = new Application();

        long startTime = System.currentTimeMillis();

        app.work();

        long endTime = System.currentTimeMillis();

        System.out.println("Geçen zaman: " + (endTime - startTime));
        System.out.println("List 1'in boyutu: " + app.list1.size());
        System.out.println("List 2'nin boyutu: " + app.list1.size());
    }

    private void work() {
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                process();
            }
        });

        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                process();
            }
        });

        thread1.start();
        thread2.start();

        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
        }

    }

    private void process() {
        for (int i = 0; i < 1000; i++) {
            addNewIntegerToList1();
            addNewIntegerToList2();
        }
    }

    private void addNewIntegerToList1() {
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
        }

        list1.add(new Random().nextInt());
    }

    private void addNewIntegerToList2() {
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
        }

        list2.add(new Random().nextInt());
    }

}

Okumaya devam et

Java’da Multithreading – Bölüm 3: “synchronized” Anahtar Kelimesi

İkinci bölümde threadlerin senkronizasyonu sırasında karşılaştığımız zorlukların birincisinden bahsetmiştik. Bu bölümde bu zorlukların ikincisini ve onu aşmak için Java’nın bize sağladığı “synchronized” anahtar kelimesini kullanmayı örnekleyeceğiz. Önce zorluğun/problemin ne olduğunu yine bir örnek ile açıklamaya çalışalım:

public class Application {

    private int count = 0;

    public static void main(String[] args) {
        Application application = new Application();
        application.doCount();
    }

    private void doCount() {
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    count++;
                }
            }
        });

        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    count++;
                }
            }
        });

        thread1.start();
        thread2.start();

        System.out.println("Sayaç: " + count);
    }

}

“Application” sınıfının “count” (sayaç) adlı, başlangıç değeri 0 olan tam sayı tipinde bir örnek değişkeni vardır. “main” metodu içinde bir “Application” nesnesi oluşturarak “doCount()” çağrılır. “doCount()” metodu içinde, “run()” metodu içerisinde “count” değişkenini 10000 kere arttıracak birer döngü içeren iki thread oluşturulur ve başlatılır. Metodun sonunda ise “count” değişkeninin son değeri yazdırılır. Buraya kadar her şey açık. Ancak bu programı çalıştırdığımızda bizi bir sürpriz beklemektedir. Program sonlandığında konsolda 20000 görmeyi bekleyenlerimiz olacaktır. Evet bazen 20000 görebiliriz de. Ancak 511 gibi, 10987 gibi saçma sayılar gördüğünüzde siz de benim gibi şaşırmışsınızdır. Sebebi şudur: “thread1” ve “thread2” ayrı threadler oldukları için “start()” metotlarıyla başladıktan sonra ana thread, yani bu iki threadi başlatan o anki geçerli thread işleyişine devam eder ve “count” değişkeninin anlık değerini konsola yazdırır. Bu sırada “thread1” ve “thread2” sonlanmamış olabilecekleri için sayacın son değeri bazı durumlar için 20000 olamayabilir. Bu durumu çözmek için bu iki threadin “join()” metodunu çağırarak threadlere, çalışmaları bitene kadar “geçerli threadi” bekletmelerini söyleyebiliriz: Okumaya devam et