Thread Ama Ne Zaman

584

Bir önceki makalemizde, thread neden kullanılır ve nasıl tanımlanır konularını anlamaya çalıştık. Bu makalemizde ise, thread oluşturmayı gerektiren durumları, bir uygulama üzerinden ve uygulamayı adım adım geliştirerek göreceğiz. Her adım da bir problemi görüp, çözümünü bulacağız. Bulduğumuz çözümler sayesinde de java thread konusunun diğer başlıklarını hep beraber öğreneceğiz.

Bunun için iki java uygulaması yazacağız. İlk uygulama bir sunucu uygulaması olacak. İkinci uygulamamız da sunucuya bağlanan ve aldığı veriyi işleyen bir uygulama olacak.

Sunucu uygulamamız bize yapısal olmayan, virgül ile ayrılmış string tipinde veriler gönderecek. Yapısal olmayandan kastım; sınıflandırması yapılmamış veri.

Veri örneklerimiz aşağıdaki gibi 3,4 ve 5 uzunluklu string ifadeler olacak.

A,B,C
A,B,C,D
A,B,C,D,E

Sunucu uygulamanın örneğimizde yapacağı tek iş bu olacak.

İstemci uygulamamız bu verileri soket üzerinden alacak ve karakter uzunluğuna göre sınıflandıracak.

Sınıflandırma mantığımız ve etiketlerimiz;

3 –> X
4 –> Y
5 –> Z

Etiketlenen her veri için de bir tekil id oluşturacağız. Şimdi kodlamaya geçelim.

İlk olarak sunucu uygulamamızı yapalım. Sunucumuz TCP protokolü ile haberleşen bir uygulama olacak. Java da ağ programlama da kullandığımız sınıflar java.net paketinin altındadır.

Sunucu uygulamamızın kodu aşağıdaki gibi olacak.

Şimdi ikinci uygulamamızı geliştirmeye başlayalım. Uygulamamızı problemleri görüp çözümleri uygulayarak geliştireceğimizi belirtmiştik.
Sunucuya bağlanmak için bir Soket sınıfı yazacağız. Aldığımız verileri işleyecek bir DataProcessor sınıfı yazacağız. İşlediğimiz veriyi LabeledEntity örnekleri içerisine ekleyeceğiz.

Soket.java sınıfı:

Soket.java sınıfımız Runnable interface’ini implemente etti. Yani bu sınıfımız artık ayrı bir thread üzerinden çalışacak haldedir.
Sunucuya bağlanıp veriyi alıyoruz. Sonrasında DataProcessor sınıfının proceedData metoduna aldığımız veriyi gönderiyoruz. Metot bittikten sonra var ise bir sonraki veriyi alıyoruz. Bu işlem her veri alındığında tekrarlanıyor.

DataProcessor.java sınıfı:

proceedData metodu içerisinde veriyi “,” ile split ediyoruz ve oluşan dizinin boyutuna göre sınıflandırma yapıyoruz. Metot içerisine veri üzerinde yapılan işlemlerin tükettiği ortalama bir süre sağlamak için 2 saniye bekleten kodu ekledik.

LabeledEntity sınıfı ve DataType enum’u:

ClientStarter.java sınıfı:

Artık uygulamalarımızı çalıştırabiliriz. İlk olarak DataSender isimli sunucu uygulamamızı ardından DataClassifier isimli asıl uygulamamızı çalıştırıyoruz. Sonuç aşağıdaki gibi olacak.

 

Problem:
Uygulamamız 2 saniyede bir yeni veri alabiliyor. Bu durum sunucu tarafında veri birikmesine sebep oluyor. Uygulamamızın veri alma süresinin iyileştirilmesi gerekmekte.

Çözüm:
1-İlk olarak sunucudan veriyi alır almaz bir java koleksiyonuna ekleyelim ve hemen bir sonraki veriyi alalım. Cevap dönme süremizi kısaltalım.
2-Koleksiyon içerisine eklediğimiz verileri okuyabilmesi için , DataProcessor sınıfını ayrı bir thread olarak çalıştıracak kod değişikliğini yapacağız.

Bu değişiklikleri ilgili sınıf isminin sonuna V2 getirerek yapacağız. Geliştirmemiz bittiğinde,

SoketV2.java
DataProcessorV2.java
ClientStarterV2.java
DataQueue.java

sınıflarını oluşturmuş olacağız.

SoketV2.java sınıfı:

Yaptığımız değişiklik, DataQueue isimli sınıfımızın add metodunu çağıran kodu ekledik, DataProcessor sınıfını ise kaldırdık. Bu şekilde, gelen veri hemen queue ya ekleniyor ve bir sonraki veri alınıyor.

DataProcessorV2.java sınıfı:

DataProcessorV2 sınıfımız artık ayrı bir thread de çalışacak. Sınıfının run metodunda while ile sonsuz bir döngü oluşturduk. Çünkü uygulama çalıştığı sürece, DataProcessor thread’i sürekli queue ya bakmalı. DealQueue sınıfının açıklamasında, hangi java koleksiyon nesnesini ve kullanma nedenimizi açıklayacağız. proceedData metodumuz da public den private a çevrildi.

DataQueue.java sınıfı:

Bu sınıfımızın içerisine BlockingQueue tipinde bir koleksiyon ekledik. BlockingQueue tipi java.util.concurrent paketinin altında bulunur. Sınıfımıza delegasyon yöntemi ile, BlockingQueue tipine ait add ve take metotlarını ekledik. add metodu, queue eleman eklerken, take metodu queue’dan eleman alır. take metodunun bizim için en önemli özelliği, eğer queue boş ise, akış burada durdurulur ve queue’ya eleman ekleninceye kadar bekler. Sonsuz bir döngü ile eğer bir koleksiyonu kontrol eden bir kodumuz var ise, bizim için en önemli konu koleksiyon boş iken kodun devam etmemesidir. Çünkü kod devam eder ise, thread hiç bir iş yapmazken bile CPU tüketir. Buda bizim için istenmeyen bir durumdur.

ClientStarterV2.java sınıfı:

DataProcessor sınıfımızı da thread olarak çalıştırdır. Artık her iki uygulamayı da çalıştırıp sonuçlara bakabiliriz.

Sonuç:

Sonuçlardan da gördüğümüz üzere problemimizi çözdük. Ama bu seferde gelen veriler bizim uygulamamızda birikiyor. Bir sonraki makalemizde inşallah bu problem üzere odaklanıp çözmeye çalışacağız.

Örnek Uygulama

Şimdilik bu kadar. İnşallah faydalı olmuştur.

İyi Çalışmalar

Yorum yaz

Email adresiniz yayınlanmayacaktır.