Java’da Multithreading – Bölüm 7: Producer-Consumer Yapısı

Producer (Üretici) ve Consumer (Tüketici) yapısı hem günlük hayatta hem de programlama yaparken sıkça karşılaşabileceğimiz bir yapıdır. Producer kuyruğa bir şeyler ekler, Consumer ise bu kuyrukta bir şeyler oldukça sırayla alır ve ne yapması gerekiyorsa yapar. Günlük hayattan bir örnek verecek olursak; bir bankada müşteriler için sıra numarası üreten cihaza Producer, işlem görmemiş sıra numaralı müşteri oldukça onları LED sıra göstergeleri aracılığıyla çağırıp işlem yapan vezne görevlisi de Consumer olarak düşünülebilir.

Siz de fark etmişsinizdir, Producer ve Consumer birbirinden bağımsız iki ayrı thread halinde gerçekleştirilebilir. Ancak burada yine ortak bir veri kaynağımız olduğunu gözden kaçırmayalım: Kuyruk (Queue). Yine yardımımıza yetişen java.util.concurrent.* paketinden “thread-safe” bir yardımcı sınıf olan “BlockingQueue” oluyor. BlockingQueue aslında bir arabirim (interface) ve biz onun gerçekleştirimlerinden (implementation) biri olan “ArrayBlockingQueue” sınıfını kullanacağız. ArrayBlockingQueue tipinde bir kuyruk oluştururken bir kapasite belirtiriz. Bu kapasite dolu ise kuyruğa yeni eleman eklemek isteyen thread bekletilir. Benzer şekilde eğer kuyrukta eleman yoksa kuyruktan eleman almak isteyen thread kuyrukta eleman oluncaya kadar bekletilir.

Şimdi Producer-Consumer yapısını ve ArrayBlockingQueue sınıfının kullanımını örnekleyeceğimiz programımızı inceleyelim:

public class Application {

    private static BlockingQueue queue = new ArrayBlockingQueue<>(10);

    public static void main(String[] args) throws InterruptedException {
        Thread producerThread = new Thread(() -> {
            produce();
        });

        Thread consumerThread = new Thread(() -> {
            consume();
        });

        producerThread.start();
        consumerThread.start();

        producerThread.join();
        consumerThread.join();
    }

    private static void produce() {
        Random random = new Random();
        while (true) {
            try {
                queue.put(random.nextInt(100));
            } catch (InterruptedException e) {
            }
        }
    }

    private static void consume() {
        while (true) {
            try {
                Thread.sleep(100);
                Integer value = queue.take();
                System.out.print("Alınan sayı: " + value + ", Kuyruğun boyutu: " + queue.size());
            } catch (InterruptedException e) {
            }
        }
    }

}

Öncelikle 10 eleman kapasiteli ve elemanları tam sayı olacak olan bir kuyruk oluşturuyoruz. Ardından main metodu içerisinde biri “produce()” diğeri “consume()” metodunu çağıracak iki adet thread oluşturup başlatıyoruz. “procude()” metodu bir sonsuz döngü içerisinde rastgele sayılar oluşturarak kuyruğa ekliyor. “consume()” metodu ise yine bir sonsuz döngü içerisinde (rahat gözlemleyebilmemiz için 100 mili saniye bekleyerek) kuyruktan bir sayı alıyor ve bu sayı ile kuyruğun o anki boyutunu yazdırıyor. Programı çalıştırdığımızda çıktı aşağıdakine benzer şekilde olacaktır:

Alınan sayı: 44, Kuyruğun boyutu: 9
Alınan sayı: 34, Kuyruğun boyutu: 10
Alınan sayı: 51, Kuyruğun boyutu: 10
Alınan sayı: 49, Kuyruğun boyutu: 10
Alınan sayı: 18, Kuyruğun boyutu: 9
Alınan sayı: 37, Kuyruğun boyutu: 9
Alınan sayı: 88, Kuyruğun boyutu: 10
Alınan sayı: 94, Kuyruğun boyutu: 9
Alınan sayı: 99, Kuyruğun boyutu: 9
Alınan sayı: 95, Kuyruğun boyutu: 9
Alınan sayı: 69, Kuyruğun boyutu: 10
Alınan sayı: 72, Kuyruğun boyutu: 9
Alınan sayı: 96, Kuyruğun boyutu: 9
Alınan sayı: 72, Kuyruğun boyutu: 10
Alınan sayı: 56, Kuyruğun boyutu: 10
Alınan sayı: 92, Kuyruğun boyutu: 10
Alınan sayı: 89, Kuyruğun boyutu: 9
...

Consumer 100 mili saniyede bir kuyruktan eleman aldığı için o 100 mili saniye geçene kadar Producer kuyruğu çoktan doldurmuş oluyor. Consumer kuyruktan bir eleman aldıkça eş zamanlı çalışan Producer bazen Consumer kuyruğun boyutunu henüz yazdırmamışken yerine yenisini eklemiş olabiliyor. Bu nedenle kuyruğun boyutu genelde 9 veya 10 olarak yazdırılmakta.

Programın çalışmasını rahatça gözlemleyebilmemiz için koyduğumuz “Thread.sleep(100)” çağrısını kaldırırsak kuyruk boyutunun zaman zaman farklılaştığını, hatta bazen sıfırlandığını görebiliriz:

...
Alınan sayı: 71, Kuyruğun boyutu: 9
Alınan sayı: 81, Kuyruğun boyutu: 8
Alınan sayı: 87, Kuyruğun boyutu: 7
Alınan sayı: 69, Kuyruğun boyutu: 6
Alınan sayı: 5, Kuyruğun boyutu: 5
Alınan sayı: 15, Kuyruğun boyutu: 4
Alınan sayı: 94, Kuyruğun boyutu: 3
Alınan sayı: 6, Kuyruğun boyutu: 2
Alınan sayı: 0, Kuyruğun boyutu: 1
Alınan sayı: 95, Kuyruğun boyutu: 0
Alınan sayı: 14, Kuyruğun boyutu: 9
Alınan sayı: 4, Kuyruğun boyutu: 9
Alınan sayı: 9, Kuyruğun boyutu: 9
...
Reklamlar

Bir Yorum Yazın

Aşağıya bilgilerinizi girin veya oturum açmak için bir simgeye tıklayın:

WordPress.com Logosu

WordPress.com hesabınızı kullanarak yorum yapıyorsunuz. Log Out / Değiştir )

Twitter resmi

Twitter hesabınızı kullanarak yorum yapıyorsunuz. Log Out / Değiştir )

Facebook fotoğrafı

Facebook hesabınızı kullanarak yorum yapıyorsunuz. Log Out / Değiştir )

Google+ fotoğrafı

Google+ hesabınızı kullanarak yorum yapıyorsunuz. Log Out / Değiştir )

Connecting to %s