Java’da Multithreading – Bölüm 1: Merhaba Thread Kardeş

Thread başka threadlerle eş zamanlı çalışabilen bir iş parçacığı olarak ifade edilebilir. Türkçe’ye çevirecek olursak “multithreading”i “eşzamanlılık”, “thread”i ise “iş parçacığı” olarak ifade edebiliriz.

Burada Java’da threadler oluşturmanın ve çalıştırmanın iki basit yolunu göreceğiz.

Birinci yol “Thread” sınıfını genişleten (extend) bir sınıf oluşturmak ve “run()” metodunu gerçekleştirerek (implement) threadin yapmasını istediğimiz işi bu “run()” metodu içerisinde kodlamaktır:

class Runner extends Thread {

    @Override
    public void run() {
        // Thread çalıştığında yapılması istenen işlemler buraya yazılır
    }

}

Diyelim ki “run()” metodumuzun gerçekleştirimi şu şekilde olsun:

@Override
public void run() {
    for (int i = 0; i < 10; i++) {
        System.out.println("Merhaba " + i);
    }
}

Şimdi threadimizin çalışmasını test etmek için “main” metoduna sahip bir sınıf oluşturalım. Bir Runner örnek değişkeni oluşturup “start()” metodu ile threadi işletelim:

public class Application {

    public static void main(String[] args) {
        Runner runner = new Runner();

        runner.start();
    }

}

Çıktı şu şekilde olacaktır:

Merhaba 0
Merhaba 1
Merhaba 2
Merhaba 3
Merhaba 4
Merhaba 5
Merhaba 6
Merhaba 7
Merhaba 8
Merhaba 9

DİKKAT! Threadi çalıştırmak için “run()” metodu yerine “start()” metodunu kullanıyoruz. “run()” metodunu çağırmamız halinde “runner”ın ifade ettiği işlemler ayrı bir thread içinde çalıştırılmak yerine o an geçerli thread içerisinde yani “main” metodunu çalıştıran ana thread içerisinde çalışacaktı. “start()” metodunu çağırdığımızda ise “runner”ın ifade ettiği işlemler kendi threadi içerisinde çalıştırılır.

Buraya kadar bir sürpriz yok. “run()” içine yazdığımız o döngüyü “main” metodu içine yazsak da aynı çıktıyı alırdık. Farkı görmek için bir çoklu thread (multi thread) örneği yapalım:

public static void main(String[] args) {
    Runner runner1 = new Runner();
    Runner runner2 = new Runner();

    runner1.start();
    runner2.start();
}

“main” metodu içinde iki adet thread oluşturduk ve onları birbiri ardına başlattık. Bu sefer çıktı aşağıdakine benzer şekilde olacaktır. (Eş zamanlılıktan dolayı, her çalıştırdığınızda çıktının biraz değiştiğini görebilirsiniz.)

Merhaba 0
Merhaba 1
Merhaba 0
Merhaba 1
Merhaba 2
Merhaba 3
Merhaba 4
Merhaba 5
Merhaba 6
Merhaba 7
Merhaba 8
Merhaba 9
Merhaba 2
Merhaba 3
Merhaba 4
Merhaba 5
Merhaba 6
Merhaba 7
Merhaba 8
Merhaba 9

“run()” metodu içindeki for döngüsünü threadin adınını yazdıracak şekilde düzenleyip threadlerin birbirini beklemeden eş zamanlı çalışarak birbirlerinden bağımsız olarak nasıl çalıştıklarını görelim:

for (int i = 0; i < 10; i++) {
    System.out.println("Merhaba " + i + " - " + this.getName());
}

Çıktı aşağıdakine benzer şekilde olacaktır:

Merhaba 0 - Thread-0
Merhaba 1 - Thread-0
Merhaba 2 - Thread-0
Merhaba 3 - Thread-0
Merhaba 0 - Thread-1
Merhaba 1 - Thread-1
Merhaba 2 - Thread-1
Merhaba 3 - Thread-1
Merhaba 4 - Thread-1
Merhaba 5 - Thread-1
Merhaba 6 - Thread-1
Merhaba 7 - Thread-1
Merhaba 8 - Thread-1
Merhaba 4 - Thread-0
Merhaba 9 - Thread-1
Merhaba 5 - Thread-0
Merhaba 6 - Thread-0
Merhaba 7 - Thread-0
Merhaba 8 - Thread-0
Merhaba 9 - Thread-0

Çıktıdan da anlaşılacağı üzere Thread-1 yani “runner2” çalışmak için Thread-0’ı yani “runner1”i beklemedi.

İşte eş zamanlı programlamayı önemli kılan nokta da bu. Şöyle ki; işlemci çekirdekleri kendilerine atanan işlemleri birbirinden bağımsız olarak, eş zamanlı çalıştırdı. Böylece normalde tek çekirdek ile 2T sürede bitebilecek iş T sürede bitirildi.

Yukarıdaki “DİKKAT!” notumuza dönecek olursak, eğer “start()” yerine “run()” metodunu kullansaydık, işlemler ayrı threadler yerine o an geçerli thread içinde çalıştırılacağından iki Runner örnek değişkeninin ihtiva ettiği işler her zaman sırayla çalıştırılacaktı. Yani önce “runner1” sonra “runner2” koşturulacaktı. Yani şöyle bir “main“ metodu için,

public static void main(String[] args) {
    Runner runner1 = new Runner();
    Runner runner2 = new Runner();

    runner1.run();
    runner2.run();
}

çıktı her zaman aşağıdaki gibi olacaktır:

Merhaba 0 - Thread-0
Merhaba 1 - Thread-0
Merhaba 2 - Thread-0
Merhaba 3 - Thread-0
Merhaba 4 - Thread-0
Merhaba 5 - Thread-0
Merhaba 6 - Thread-0
Merhaba 7 - Thread-0
Merhaba 8 - Thread-0
Merhaba 9 - Thread-0
Merhaba 0 - Thread-1
Merhaba 1 - Thread-1
Merhaba 2 - Thread-1
Merhaba 3 - Thread-1
Merhaba 4 - Thread-1
Merhaba 5 - Thread-1
Merhaba 6 - Thread-1
Merhaba 7 - Thread-1
Merhaba 8 - Thread-1
Merhaba 9 - Thread-1

Bir thread oluşturmanın ve başlatmanın ikinci yolu ise Thread kurucusuna Runnable arabirimini (interface) gerçekleştiren bir sınıf örneği geçmektir.

Runnable arabirimi tek bir metot imzası içerir:

void run();

Runner sınıfımızı Runnable arabirimini gerçekleştiren bir sınıf olarak yeniden yazarsak:

class Runner implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("Merhaba " + i);
        }
    }

}

Bu durumda “main” metodumuz şu şekilde değişecektir:

public static void main(String[] args) {
    Thread thread1 = new Thread(new Runner());
    Thread thread2 = new Thread(new Runner());

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

Yeni threadler oluşturmak için Thread sınıfının uygun kurucusuna Runnable arabirimini gerçekleştiren bir sınıfın örneği geçtiğimiz dikkatinizi çekmiştir. Yukarıdaki “main” metodu çalıştırıldığında, threadler eş zamanlı çalışacağından, çıktımız yine aşağıdakine benzer şekilde -düzensiz- olacaktır:

Merhaba 0
Merhaba 1
Merhaba 2
Merhaba 0
Merhaba 1
Merhaba 2
Merhaba 3
Merhaba 4
Merhaba 5
Merhaba 6
Merhaba 7
Merhaba 8
Merhaba 9
Merhaba 3
Merhaba 4
Merhaba 5
Merhaba 6
Merhaba 7
Merhaba 8
Merhaba 9

Eğer Runnable gerçekleştirimini bir kere kullanacaksak anonim sınıf da (anonymous class) kullanabiliriz:

Thread thread = new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("Merhaba");
    }
});

Eğer Java 8 kullanıyorsanız, yukarıdaki kullanımı bir adım daha ileri götürerek Lambda kullanabilirsiniz. Runnable arabirimi bir @FunctionalInterface olduğu için, Runnable arabirimi bekleyen herhangi bir metoda Runnable arabiriminin yapısına uygun bir Lambda geçilebilir. Lambda kullandığımızda bakın nasıl da o 6 satırlık kod parçası tek satırlık bir kod parçasına dönüşüyor:

Thread thread = new Thread(() -> System.out.println("Merhaba"));
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