Günümüzde hemen her cihaz akıllanmaya basladı. Ve gelişmeyi desteklemek için giderek çok çekirdekli işlemcilerle desteklenmeye başlandı. Uygulamalarımızda bu gücü kullanmak yazılım uzmanları olarak bizim elimizde.

Yazımda bu büyük işlem gücünü kullanmamızı sağlayan multi-thread uygulamaları Java’da nasıl geliştireceğimizden bahsedeceğim.

Thread, Türkçe iş parçacığı anlamına geliyor. Çok fazla detaya girmeyeceğim. Merak edenler multi-process ve multi-threading kavramlarını derinlemesine araştırabilir. Benim amacım bu kavramları Java’da nasıl kullanacağımızla sınırlı olacak. Her Java uygulaması oluştuğunda bir adet thread oluşturur. Bu thread main bloğumuz tarafından kullanılır. Ancak zaman alacak veya farklı parçalara bölmek isteyeceğimiz işlemlerimiz için bizde bir veya daha fazla thread tanımlayabiliriz.

Örneğin bir AI (yapay-zeka) uygulamasında algoritma tarafından hesaplanacak veriler birden fazla thread içinde hesaplanarak çok çekirdeğin gücünden faydalanılabilinir ve bu şekilde performans artımı elde edilir. Veya client-server (sunucu-istemci) uygulamasında istemcinin sunucu ile bağlantısını thread içinde gerçekleştirerek bu sırada kullanıcının farklı işlemler yapabilmesi sağlanabilir.

Peki Java ile nasıl bir thread oluştururuz? Bunun farklı yolları bulunuyor. Bir tanesini aşağıda inceleyebilirsiniz.

public class Main {
  public static void main(String[] args) {
    System.out.println("I am main thread.");

    // Yeni bir thread yaratıyoruz
    Thread thread = new Thread(new Runnable() {
      // Thread çalıştığında girilecek olan kod bloğunu hazırlıyoruz
      @Override
      public void run() {
        System.out.println("I am a child thread.");
      }
    });
    // Thread'ı çalıştırıyoruz
    thread.start();

    System.out.println("Program exits.");
  }
}

Bu uygulamanın çıktısı aşağıdaki gibidir.

I am main thread.
Program exits.
I am a child thread.

Bu çıktıdan çıkartmamız gereken çok önemli bir sonuç var. Ana thread, yeni bir thread oluşturup çalıştırdığında onun çalışmasını beklemez ve kendisi ilerlemeye devam eder. İşlemci uygun olduğunda yeni oluşan thread’in çalışmasına izin verilir. Burada da olan tam olarak bu.

Farklı bir metot izleyerek benzer bir thread oluşturalım.

public class Main {
  public static void main(String[] args) {
    System.out.println("I am main thread.");

    // İş yapacak olan objemizi yaratıyoruz.
    Worker worker = new Worker();
    // Thread'e yüklüyoruz.
    Thread thread = new Thread(worker);
    // Thread'ı çalıştırıyoruz.
    thread.start();

    // Thread'ın bitmesini bekliyoruz.
    synchronized (thread) {
      try {
        thread.wait();
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }

    System.out.println("Program exits.");
  }
}

// Runnable arayüzünü implement eden bir sınıf hazırlayalım.
class Worker implements Runnable {
  // Runnable çalıştığında girilecek olan kod bloğunu hazırlıyoruz
  @Override
  public void run() {
    System.out.println("Starting process.");

    // Çok büyük bir işlem simule ediyoruz.
    synchronized (this) {
      try {
        MyRunnable.this.wait(1000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }

    System.out.println("End of process.");
  }
}

Bu programın çıktısı aşağıdaki gibidir.

I am main thread.
Starting process.
End of process.
Program exits.

Buradan bir thread’ı nasıl oluşturacağımızı ve onun çalışmasını nasıl kontrol edebileceğimizi en basit hatları ile öğrenmiş olduk. Daha sonra Thread senkronizasyon hakkında daha fazla detay içeren bir yazı hazırlayacağım.

Başka bir yazıda görüşmek üzere..