JAVA Programlama

Java’ da Çok Kanallı (Multithread) İşlemler

image_pdfimage_print

Java’ da Çok Kanallı (Multithread) İşlemler:

Çok Kanallı Programlama (Multi-Threaded Programming), bir programda aynı anda birden fazla işin yapılabilmesidir. Yani bir kod parçası bir işlemi gerçekleştirirken aynı anda ona paralel olarak bir başka kod parçasının çalışması demektir. Birbirine paralel çalışan çalışanlardan her birine kanal (thread)

Java çok kanallı porgramlamayı temelden desteklemektedir. Yani çok kanallılık dile bir takım kütüphanelerle eklenmemiştir. Aslında her Java programı bir kanalda (thread’te) çalışır. Örneğin application’ların main method’unun çalıştırılmasıyla adı ‘main’ olan bir ana thread çalıştırılır. Ancak tek bir kanal olunca programcı yazdığı kodun bir thread’in içerisinde çalıştığını farketmez. Thread bize eş zamanlı birden fazla işi yapma fırsatını yani multitasking sağlıyor.

Thread Nedir ?

  • Kelime anlamı olarak İş parçacığı gelmektedir.
  • Bir programın paralel olarak çalışan ve bir birine bağımlı ya da bağımsız işlemler yapan alt parçacıklarına verilen isimdir

Thread’in faydaları:

  • Kaynak paylaşımı
  • Daha az bekleme süresi
  • Daha verimli donanım kullanımı(Multiprocessor kullanımı)

Yeni bir thread yaratıldığında biz start komutu vermedikçe thread çalışmaya başlamıyor hazır durumda bekliyor. Biz start komutunu verdiğimizde çalışmaya başlıyor. Ayrıca thread’i sleep komutu ile belli süre bekletebiliyor, stop komutu ile öldürebiliyoruz.  Peki ama thread ile multithread arasında ne fark vardır?

Resimde gördüğümüz gibi threadler aynı kodu, veriyi ve dosyaları kullanmasına rağmen farklı register ve stack’leri vardır. Bu sayede aynı kod içinde farklı işler yapıp farklı şeyleri hafızada tutabiliyor. Java’da thread yaratmanın iki yolu var. Ben implement ederek kullandım. Ancak extend ederek de thread yaratılabilir. Thread pool diye bir kavram da var. Siz belirli bir thread sayısı veriyorsunuz. Thread pool boşta olan thread’i kullandırıyor. Multihreading yaşanabilecek en önemli sorunlardan biri, concurrent(eş zamanlılık) ve senkronizasyon sorunu. Siz aynı anda hem ekleme hem de silme yapmamınız gerekiyor. Aksi takdirde istediğinizi eklemeden hangi elemanı almaya çalışırsanız hangisini alacaksınız gibi bir soru karşımıza çıkıyor. Bunu çözmenin 3 yolu var:

  • Producer-consumer problemi: İşletim sistemleri dersinde öğrendiğimiz aynı mantık üretici-tüketici problemiyle çözebiliriz.
  • Circular buffer: Producer-consumer problemi benzer bir çözüm. Farkı kendiniz bir dizi oluşturuyorsunuz ve kilit kontrolü bu dizide oluyor.
  • Java collections: Kendimiz kilitlerle uğraşmadan bu işi java’ya bırakabiliriz. Java’nın bu konulara kendi çözüm getirdiği collections sınıfını kullanabiliriz.

Thread Oluşturma Şekilleri :

  1. Çekirdek Uzayında Gerçeklenen Threadler : Thread tablosu kernel’de (işletim sisteminde) yer alır. Hangi thread’in çalışacağına kernel (çekirdek) karar verir. Yeni thread oluşturmak için kernel’da sistem çağrısı yürütülür.
  2. Kullanıcı Uzayında Gerçeklenen Threadler : Kernel, threadlerden haberdar değildir, bu nedenle threadleri desteklemeyen işletim sistemlerinde bile kullanıabilir. Her process’in (programın) ayrı thread tabloları bulunmaktadır. Bu sayede her processte farklı iş sıralama algoritmaları kullanılabilmektedir.
  3. Hibrit Threadler : Çekirdek ve kullanıcı düzeyinde threadler vardır, ancak kernel sadece kernel düzeyindeki threadlerden haberdardır. Bir kernel düzeyi thread üzerinde birden fazla kullanıcı düzeyi thread sıra ile çalışır.

Java’da kanal (thread) oluşturmak için öncelikle paralel çalışacak olan program parçamızı ‘Thread’ türü bir sınıf olarak tanımlamamız gerekir. Bu sınıf içinde çağrılmadan çalışacak bir metod olması gerekir. Bu da ‘Thread’ sınıfında tanımlanmış olan ‘run’ metodudur. Yapmamız gereken sadece bu metodu değiştirerek (override) istediğimiz kodu bunu içine yerleştirmektir.

Daha sonra programın bu kanalı gören herhangi bir noktasından ‘Thread’ sınıfının ‘start’ metodu çalıştırılarak. Thread çalışmaya başlatılabilir. Bu metod kendi içinde (implicitly), ‘run’ metodunu çağıracak ve dolayısıyla kanala yaptırmak istediğimiz işler yapılmaya başlıyacaktır:

Kanalın ‘run’ metodu içinde genellikle gecikmeler içeren döngüler kullanılır:

Burada ‘Thread’ sınıfının ‘sleep’ metodu ile kanal belli bir süre sonra çağrılmak üzere bekleme kuyruğuna alınır. Bu sayede diğer kanallar da çalışma fırsatı bulurlar. Aynı şekilde ‘sleep’ yerine aynı sınıfın ‘yield’ metodu kullanılarak işletimi diğer kanallara geçirmek de mümkündür. Aşağıdaki adreste çok kanallı bir ugulama paralel hareket eden butonlarla örneklenmiştir:

Bazen kanalları yaratırken ‘Thread’ sınıfını devralmak (inheritance) uygun olmayabilir. Bu durumda ‘Runnable’ arabirimi (interface) kullanılarak da çalıştırılabilir (runnable) bir sınıf yaratılabilir:

Burada görüldüğü gibi ‘sleep’ gibi metodlar ‘Thread’ sınıfından devralınmadığı için ancak ‘Thread’ sınıfının statik metodları olarak çağrılabilmekte. Görüldüğü gibi bu durumda yarattığımız sınıf ‘Thread’ sınıfına ait özellik ve metodlara sahip değildir ve bunu kanal gibi belli aralıklarla paralel çalıştıramayız. Ancak ürettiğimiz çalıştırılabilir kesimi yaratacağımız bir kanal nesnesine gömerek tam bir kanal elde edebiliriz. Kanalı yaratıp çalıştırmak şu şekilde mümkün:

Aşağıda bu tür kanal yaratmaya örnek verilmiştir:

 

Thread Türleri :

  1. Single Threading :Aynı anda tek thread çalıştırma işlemidir. Threading’i desteklemeyen işlemciler tarafından kullanılan yöntemdir. Uygulama multithread olarak hazırlansa dahi ana thread üzerinden işlemleri çalıştıracaktır. Günümüzde bu teknolojide işlemciler kalmamıştır. (MS-DOS)
  2. Multi Threading : Aynı anda birden fazla thread çalıştırma işlemidir. Az önce verdiğimiz örnekteki gibi ve çok daha gelişmiş yapılar bu işlem için örnek verilebilir. Günümüz işlemci ve yazılım mimarisi bu sistem üzerine kurulmuştur. (Solaris, UNIX, Windows)

 

Threadlerin Yaşam Döngüsü :

 

 

 

 

 

 

 

 

 

 

 

 

New; Yaratmis oldugumuz thread’in hazir beklemesi durumudur.

Runnable; Yaratmis oldugumuz thread’in artik calisir duruma gelmesidir.

Blocked; Thread bloklanarak, monitor lock durumu icin bekletilmektedir. Blok tekrar acildiginda yeniden runnable durumuna gececektir.

Waiting; Bir thread’in suresiz olarak diger thread’lerin yapmis oldugu isleri tamamlayabilmesi icin o thread’in bekledigi durumdur.

Timed Waiting; Bir thread’in diger thread’in isini tamamlayabilmesi icin sadece belirli bir sure bekledigi durumdur.

Terminated; Thread’lerin tum islerinin tamamlandigi ve islemlerin sonlandigi durumdur.

 

Java da Thread İşlemleri

2 şekilde thread’leri çağırabilir. Kalıtım ile (extends) thread kütüphanesini çağırarak . 2. şekil olarak’ta throws InterruptedException ile main class’ında çağırabiliriz.

Metodlar : Anlamları

  • isAlive() :  Bir kanalın ayakta olup olmadığını öğrenmek için kullanırız
  • sleep(long/bu kısma değer girilicek) : Kanal’ın uyumasını sağlayabilirz. Thread.sleep(long) ile de kanal içerisinde uyuma sağlarız.
  • synchronized: Kanala yapılan metod çağrılarında sıralı işlemler için kullanılır.
  • wait(): kanalı beklemeye alırız.
  • notify(): Kanalı uyandırırız.
  • setPriority(int): Kanalların çalışma anlarında öncelik için kullanılır.
  • yield(): Boşta duran kanalların işgalini önlemek için kullanırız. Bekleme sırasında yüksek veya eşit önceliğe sahip kanalların çalışmasına izin verir.
  • join(): kanalın belirli süre veya ölene kadar beklemesini sağlarız.
  • interrupt():  wait veya sleep halinde olabilen kanalın uyandırılmasını sağlamaktadır. Kanalın kesme durumunda olup olmadığına da isInterrupted() ile bakılır.
  • stop() ya da destroy(): Kanalı öldürmek için kullanırız.

 

Kanallar Arası Haberleşme

Kanallar aynı bağlamı paylaşan kod kesimleri olduğu için aynı görev içinde iseler gördükleri global bir bellek alanı mutlaka vardır. Kanallar arası iletişimde bu alanlar kullanılabilir. Aşağıda kanallar arası haberleşmede dört farklı global kullanımı örneklenmiştir:

Global alan kullanımı bazı durumlarda kanallar arasında sorunlara sebep olmaktadır. Bu sorunlar kanallar arası zaman uyumlama kapsamında ele alındı.

 

Kanallar Arası Zaman Uyumlama (Synchronization)

Kanallar global bir sayaç kulanan aşağıdaki gibi bir kanal düşünelim:

Burada tüm kanallardan toplam geçişin 10 olduktan sonra kanalların işletimini durdurmak istiyoruz. Ancak, birbirinden bağımsız çalıştıklarına göre sayaç 9 iken kanallardan biri 4. satırdaki koşulun sağlandığını görerek 5. satıra geçer. Aynı anda 4.satıra gelen başka bir kanal da aynı koşulun sağlandığını görerek 5. satıra geçer. Böylece sayacın değeri önce 10 sonra da 11 olarak programımız hatalı çalışmış olur. Bu tür durumları önlemek için kanallarda işletilecek kritik kesimler aynı anda tek bir kanalın erişebileceği yapıda oluşturulur. Aşağıdaki örnek bu sorunu çözer:

Aynı yapı aşağıdaki gibi de kurulabilir:

 

Kaynak:

https://burakisikli.wordpress.com/2009/06/16/multi-threadedcok-kanalli-programlama/

https://umiitkose.com/2015/04/java-thread-islemleri/

http://www.fibiler.com/Divisions/Ehil/Mecmua/Magazines/Articles/txt/html/article_MultiThreadingJava.html

Hazırlayan: Oğuzhan YILMAZ

2
0

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir