# İŠParçacıÄı Havuzları (ThreadPooling)
##### Executor

Thread yaratmak cidden maliyetli bir olaydır. Her Thread için sistemde belli bir kaynak ayrılır. Bu kaynaklar CPU, Hafıza gibi önemli olanlardır. Uygulamamız çalıÅırken belli miktarda bir Thread ile sınırlandırmak isteyebiliriz.
Bu nedenle Thread havuzu oluÅturup bu havuzu önceden oluÅturulmuÅ ve kullanıma hazır Thread nesneleri ile doldururuz. Böylece, performans kazanımı ve sistem kaynaklarının verimli kullanımını saÄlayabiliriz. Resim gördüÄümüz hiyerarÅik düzenin en tepesindeki Executor interface'i sadece execute adlı bir metot içerir. Bu metot;
```java
void execute(Runnable command);
```
ExecutorService interface ise Executor Interfaceini extend eder ve içerisinde threadleri iÅletip deÄer döndürebilecek metotlar bulunur.
ScheduledExecutorService interface hem ExecutorService Interfaceini extends eder, hem de schedule etmeye (planlamaya) yarar.
```java
//callable-> Runnabledan farklı olarak V tipinde bir deÄer döndürebileceÄimiz bir task oluÅturmamızı saÄlar.
//delay -> taskın gecikmesini belirleriz.
//unit -> dakika,saniye vb. gibi isteÄimizi göndeririz.(Milisaniye için TimeUnit.MILLISECONDS )
public ScheduledFuture schedule(Callable callable,long delay, TimeUnit unit);
```
ThreadPoolExecutor sınıfı dolaylı yoldan ExecutorService interfaceini implemente eder ve threadlerden oluÅan bir havuzu
yönetmeye yarar. Executors Sınıfı ise içerisinde Åu static metotları barındırır ve bizim pool oluÅturmamızı saÄlar;
```java
//BoÅta bulunan threadleri çalıÅtırmaya yarar.
//BoÅta thread bulunmuyorsa havuza yeni bir tane thread ekler.
static ExecutorService newCachedThreadPool( )
//Belli sayıda threadden oluÅan ve yeni thread ekleme yapmadan
//gerektiÄinde boÅ durumdaki threadleri çalıÅtırır
static ExecutorService newFixedThreadPool(int numThreads)
//schedule iÅlemi yapmamızı saÄlar.
static ScheduledExecutorService newScheduledThreadPool(int numThreads)
```
Bu metotlar ile ilgili birer örnek vermeden önce bu konun anlaÅılması için Callable ve Future Interfaceleri ile ilgili bilgi vermek gerekir.
##### Callable ve Future
Konu içerisinde de bahsettiÄim gibi Callable Interfacei, Runnable Interfacei gibi bize tasklar oluÅturmamızı saÄlar. Callable Interfacei tasklardan sonuç deÄeri almamızı saÄlar.
```java
public interface Callable {
V call() throws Exception;
}
```
Callable Interfacei jenerik yapıda olduÄu için oluÅtururken gönderdiÄimiz deÄer tipinde bize dönüŠyapan bir call metodu içerir. ExecutorService içerisinde de Callable tasklarımızı kullanıp bize deÄer döndürecek bir metot bulunur.
```java
Future submit(Callable task)
```
Bu submit metodu bize Future tipinde bir obje döndürür. Bizim bu dönen objeden sonucu alabilmemiz için Future içerisindeki get metodunu implemente etmemiz gerekir.
```java
V get( ) throws InterruptedException, ExecutionException;
V get(long wait, TimeUnit tu) throws InterruptedException, ExecutionException, TimeoutException;
```
Bu iki metot arasındaki fark, birinci metot dönecek sonucu sonsuza kadar bekler ancak ikinci metot ise aldıÄı iki parametre ile belli bir süre bekler. EÄer bu süre aÅılırsa TimeoutException fırlatır. Ayrıca get metodu çaÄıran sınıf ya da thread sonuç gelinceye kadar bloklanır. Åimdi Callable ve Future ile bir örnek yapalım.
```java
import java.util.concurrent.*;
public class Main {
public static void main(String[] args) throws InterruptedException, TimeoutException, ExecutionException {
System.out.println("Ana threadimiz baÅladı.");
int value=10;
Callable increment=()->{
int tempValue=value;
for (int i=0;i<5;i++){
tempValue++;
System.out.println("Increment: "+tempValue);
}
return tempValue;
};
Callable decrement=()->{
int tempValue=value;
for (int i=0;i<5;i++){
tempValue--;
System.out.println("Decrement: "+tempValue);
}
return tempValue;
};
ExecutorService executorService= Executors.newFixedThreadPool(2);
Future submitIncrement=executorService.submit(increment);
Future submitDecrement=executorService.submit(decrement);
System.out.println(value+" deÄerini 5 kere arttırma iÅlemi sonucu: "
+submitIncrement.get(500, TimeUnit.MILLISECONDS));
System.out.println(value+" deÄerini 5 kere azaltma iÅlemi sonucu: "
+submitDecrement.get(500, TimeUnit.MILLISECONDS));
//Havuzu durdurmamızı saÄlayan metottur.
executorService.shutdown();
System.out.println("Ana threadimiz bitti.");
}
}
```
###### Ãıktı
```
Ana threadimiz baÅladı.
Decrement: 9
Decrement: 8
Decrement: 7
Increment: 11
Increment: 12
Increment: 13
Increment: 14
Decrement: 6
Decrement: 5
Increment: 15
10 deÄerini 5 kere arttırma iÅlemi sonucu: 15
10 deÄerini 5 kere azaltma iÅlemi sonucu: 5
Ana threadimiz bitti.
```
Ãıktıda görüldüÄü üzere bu poolumuzdaki taskların çalıÅması tamamen iÅletim sistemi tarafından kontrol edilmektedir. Hem de newFixedThreadPool metodu için bir örnekte olmuÅ oldu.
```java
ExecutorService executor = Executors.newFixedThreadPool(15);
```
Yukarıdaki Javaâda hazır bulunan âExecutorsâ sınıfındaki ânewFixedThreadPoolâ metodunu çaÄırarak bir Thread havuzu oluÅturabilirsiniz. Ardından bize 15 Threadâin kullanıma hazır halde bulunduÄu bir havuz oluÅturup verecektir. Bu fonksiyon ayrıca bize bu havuz üzerinde Thread kullanımını yönetecek âExecutorServiceâ tipinde bir nesne verecektir. Bu nesne üzerindeki âexecuteâ fonksiyonuyla havuzdaki bir Threadâi kullanıp iÅimiz bitince tekrar sisteme iade edeceÄiz.
```java
ExecutorService executor = Executors.newFixedThreadPool(15);
QMatic qmatic = new QMatic();
for(int i=0; i < 100; i++) {
executor.execute(qmatic);
}
```
Yukarıdaki örnekte âQMaticâ isminde önceden de kullandıÄımız Runnable tipinde sıra numarası veren sınıftan bir nesne yaratıyoruz. Bu kod parçasını âexecuteâ fonksiyonuyla havuzdaki bir Threadâi kullanarak çalıÅtırıyoruz. GörüldüÄü gibi havuz 15 kapasiteli olmasına raÄmen döngüde 100 kez Thread kullanma talebi gelmiÅ. EÄer havuzda uygun boÅ bir Thread yoksa beklemede kalacaktır. Havuzdan ilk boÅa çıkan Thread nesnesini alıp çalıÅacaktır. Bir de newScheduledThreadPool kullanarak bir örnek yapalım.
```java
import java.util.concurrent.*;
public class Main {
public static void main(String[] args) throws InterruptedException{
System.out.println("Ana threadimiz baÅladı.");
CountDownLatch countDownLatch=new CountDownLatch(3);
ScheduledExecutorService executor=Executors.newScheduledThreadPool(5);
ScheduledFuture> future=executor.scheduleAtFixedRate(()->{
System.out.println("Havuz ÃalıÅıyor");
countDownLatch.countDown();
},100,300,TimeUnit.MILLISECONDS);
countDownLatch.await();
executor.shutdown();
System.out.println("Ana threadimiz bitti.");
}
}
```
###### Ãıktı:
```
Ana threadimiz baÅladı.
Havuz ÃalıÅıyor
Havuz ÃalıÅıyor
Havuz ÃalıÅıyor
Ana threadimiz bitti.
```
Sıra newChachedThreadPool örneÄine geldi bu örneÄimizi ThreadPoolExecutor ile yazacaÄız.
```java
import java.util.concurrent.*;
public class Main {
public static void main(String[] args) throws InterruptedException{
System.out.println("Ana threadimiz baÅladı.");
ThreadPoolExecutor executor= (ThreadPoolExecutor) Executors.newCachedThreadPool();
executor.execute(()->{
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
executor.execute(()->{
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
executor.execute(()->{
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println("Thread havuzumuzun boyutu: "+executor.getPoolSize());
executor.shutdown();
System.out.println("Ana threadimiz bitti.");
}
}
```
###### Ãıktı:
```
Ana threadimiz baÅladı.
Thread havuzumuzun boyutu: 3
Ana threadimiz bitti.
```
> Soru: Neden birden fazla thread kullanmak isteriz? Neden bir threadleri sınırlamaya ihtiyaç duyarız? AraÅtırınız.
##### Kaynakça
###### **1.**[Resim 1](https://www.keil.com/pack/doc/CMSIS/RTOS2/html/group__CMSIS__RTOS__SemaphoreMgmt.html)
###### 2.[Kaynak 1](https://www.baeldung.com/thread-pool-java-and-guava)
###### 3.[Kaynak 2](https://medium.com/sıfırdan-iÌleri-düzeye-java-eÄitim-serisi/multithreaded-programlama-2-kısım-6cd8411f8b2e)