Sayfalar

java etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
java etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

20 Aralık 2016 Salı

Küçük Detayların Büyük Etkileri

0 yorum
English version is at DZone

Soru:
 Aşağıdaki kod parçası ile kaç satır yazı yazılır?
public static void main(String[] args){ for (int i = Integer.MIN_VALUE; i <= Integer.MAX_VALUE; i++) { System.out.println(i); } }

a) Sonsuz sayıda
b) (2147483647 *2) +1
c) 0
d) Derleme Hatası
Birçoğumuz, dikkat etmezsek, cevabın “b” şıkkı olduğu yanılgısına kapılabiliriz. Ama aşağıdaki diğer bir kod parçası cevabın “a” şıkkı olduğuna bizi ikna edecektir.
public static void main(String[] args){ System.out.println(Integer.MIN_VALUE); System.out.println(Integer.MAX_VALUE); System.out.println(Integer.MAX_VALUE + 1); }


Çıktı:
-2147483648
2147483647
-2147483648

Yukarıdaki kod parçası, i değişkeninin değeri Integer.MAX_VALUE değerine eşit olana kadar beklendiği şekilde çalışacaktır. Bu noktada i değişkenin değeri yani Integer.MAX_VALUE ekrana yazılır. Mevcut iterasyonun kontrolü geçildikten sonra, i değişkeninin değeri 1 arttırılır. Burada i değişkeninin yeni değerinin Integer.MAX_VALUE+1 olduğunu düşünebiliriz, ama değil, yeni değer Integer.MIN_VALUE ’dur. Integer.MIN_VALUE değeri Integer.MAX_VALUE değerinden küçük olduğundan iterasyon sonsuza kadar devam eder. 
Bu durumu anlamak için byte’ların ve bit’lerin dünyasına dalmamız gerekecektir. Java’da int veri tipi 32 bit’e kadar olan değerleri tutabilmektedir. Tutabildiği en küçük değer de Integer.MIN_VALUE, yani -2147483648 değeridir. İkili sayı sistemindeki ifadesi ile 1000 0000 0000 0000 0000 0000 0000 0000 ’dır. Burada en soldaki bit, işaret bitidir. 0 değeri sayının pozitif, 1 değeri ise negatif olduğunu belirtir. Yani yukarıdaki ifadede işaret biti 1’dir. Burada da görüldüğü gibi, Java sayıları tutmak için sadece 31 biti kullanmaktadır.






Buradan edindiğimiz bilgi ile, Integer.MAX_VALUE, yani 2147483647, ikili sistemdeki ifadesi ile 0111 1111 1111 1111 1111 1111 1111 1111 değerine 1 değerini ekleyelim:
Görüdüğü gibi sonuç, Integer.MIN_VALUE değeridir.
Yukarıdaki durumu ele alacak olursak, örneğin, oluşturduğumuz bir JPA entity class’ında java.lang.Integer tipindeki bir identifier ile kodumuz bir süre(belki yıllarca) beklendiği gibi çalışsa da, veritabanında oluşturacağımız kayıtların miktarı arttıkça yukarıda bahsettiğimiz problemle karşılaşabiliriz. Hiç ara vermeden, her milisaniyede yeni bir identifier ürettiğimiz bir uygulamada, Integer tipi bizi neredeyse 2 ay idare edebilecekken, Long tipi ile 300 milyon yıl gidebiliriz.
Sonuç olarak, doğru veri tiplerinin seçimi küçük bir detay olarak düşünülebilse de, başımızı ağırtacak büyük etkilere neden olabilirler. Hocamızın, “Mühendislik becerisi uç noktalarda belli olur” sözünü de bu vesileyle  paylaşmak isterim.
ali kemal taşçı

22 Mart 2016 Salı

Java Notları – Parasal Problemler

2 yorum
English version is at DZone

İlkokulda çözmeye alıştığımız basit bir problemi bir de Java’da yazacağımız kodla çözmeye çalışalım.

Klasik problem şöyle:

Ali’nin cebinde 1 TL’si vardır, Ali cebindeki para ile bakkaldaki, 10 Kr’luk şekerlerden kaç tane alabilir?

Java Kodumuz:
public static void main(String[] args) { float alininParasi = 1.00f; float sekerinFiyati = 0.10f; int aldigiSekerlerinSayisi = 0; System.out.println("Ali'nin cebinde, " + alininParasi + " TL'si vardir."); System.out.println("Bakkaldaki sekerin tanesi " + sekerinFiyati + " TL'dir."); while (alininParasi >= sekerinFiyati) { aldigiSekerlerinSayisi++; alininParasi -= sekerinFiyati; } System.out.println("Hesaplamamiza gore, Ali bakkaldan " + aldigiSekerlerinSayisi + " seker alabilir ve cebinde de " + alininParasi + " TL'si kalir."); }
Program Çıktısı:

Ali'nin cebinde, 1.0 TL'si vardir.
Bakkaldaki sekerin tanesi 0.1 TL'dir.
Hesaplamamiza gore, Ali bakkaldan 9 seker alabilir ve cebinde de 0.09999993 TL'si kalir.

Nasıl olur?

Burada, beklentimizden farklı bir sonuç görmemizin nedeni, kodumuzda parasal değerleri float(veya abisi double) veri tiplerinde tutmamızdır. Java’da float ve double veri tipleri, bilimsel ve mühendislik hesaplamalarında, hızlı ve doğru sonuçlar elde etmek üzere tasarlanmışlardır. Bu sebepten, tam(exact) sonuçlar beklediğimiz parasal hesaplamalarda bu veri tiplerini kullanmamamız gerekir. Bunun yerine BigDecimal, int veya long veri tiplerini kullanmamız gerekir.

Yukarıdaki Java kodumuzu float yerine BigDecimal kullanarak tekrar yazalım:
public static void main(String[] args) { BigDecimal alininParasi = new BigDecimal("1.00"); BigDecimal sekerinFiyati = new BigDecimal("0.10"); int aldigiSekerlerinSayisi = 0; System.out.println("Ali'nin cebinde, " + alininParasi + " TL'si vardir."); System.out.println("Bakkaldaki sekerin tanesi " + sekerinFiyati + " TL'dir."); while (alininParasi.compareTo(sekerinFiyati) >= 0) { aldigiSekerlerinSayisi++; alininParasi = alininParasi.subtract(sekerinFiyati); } System.out.println("Hesaplamamiza gore, Ali bakkaldan " + aldigiSekerlerinSayisi + " seker alabilir ve cebinde de " + alininParasi + " TL'si kalir."); }
Program Çıktısı:

Ali'nin cebinde, 1.0 TL'si vardir.
Bakkaldaki sekerin tanesi 0.1 TL'dir.
Hesaplamamiza gore, Ali bakkaldan 10 seker alabilir ve cebinde de 0.0 TL'si kalir.

Evet şimdi oldu.

Burada BigDecimal kullanımı ile ilgili iki dezavantajdan bahsedebiliriz:
  1.  Primitive tipler kadar kullanışlı değil
  2.  Yavaş

Basit hesaplamalar için ikincisi pek canımızı sıkmasa da, ilki bazen sinir bozucu olabilir. 

BigDecimal’e alternatif olarak, int veya long veri tiplerini kullanmayı seçebiliriz. Bu durumda ondalık kısımları bizim yönetmemiz gerekiyor. Yani yukarıdaki örneğimizde hesaplamalarımızı lira olarak değil de kuruş olarak yapmamız gerekir:
public static void main(String[] args) { int alininParasi = 100; int sekerinFiyati = 10; int aldigiSekerlerinSayisi = 0; System.out.println("Ali'nin cebinde, " + (alininParasi*0.01) + " TL'si vardir."); System.out.println("Bakkaldaki sekerin tanesi " + (sekerinFiyati*0.01) + " TL'dir."); while (alininParasi >= sekerinFiyati) { aldigiSekerlerinSayisi++; alininParasi -= sekerinFiyati; } System.out.println("Hesaplamamiza gore, Ali bakkaldan " + aldigiSekerlerinSayisi + " seker alabilir ve cebinde de " + (alininParasi *0.01) + " TL'si kalir."); }
Program Çıktısı:

Ali'nin cebinde, 1.0 TL'si vardir.
Bakkaldaki sekerin tanesi 0.1 TL'dir.
Hesaplamamiza gore, Ali bakkaldan 10 seker alabilir ve cebinde de 0.0 TL'si kalir.

BigDecimal ve int veri tipleri arasındaki performans farkını, Ali’nin parasını 1.000.000 TL’ye çıkardığımız aşağıdaki program çıktılarında görebiliriz.

Program Çıktısı(BigDecimal):

Ali'nin cebinde, 1000000.0 TL'si vardir.
Bakkaldaki sekerin tanesi 0.1 TL'dir.
Hesaplamamiza gore, Ali bakkaldan 10000000 seker alabilir ve cebinde de 0.0 TL'si kalir.
islem suresi: 460 ms

Program Çıktısı(int):

Ali'nin cebinde, 1000000.0 TL'si vardir.
Bakkaldaki sekerin tanesi 0.1 TL'dir.
Hesaplamamiza gore, Ali bakkaldan 10000000 seker alabilir ve cebinde de 0.0 TL'si kalir.

ali kemal taşçı

4 Aralık 2015 Cuma

Java Notları – Overloading

0 yorum
English version is at DZone

Java 1.5  öncesinde primitive tipler, reference tiplerinden tamamen farklıydı. Ama artık autoboxing kavramıyla, bu farkın ortadan kalkması, birçok sorunu da beraberinde getirmiştir.
Bu sorunlardan birini aşağıdaki kod parçası ile açıklayalım:


import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.TreeSet; public class SetAndList{ public static void main(String[] args) { Set<Integer> set = new TreeSet<Integer>(); List<Integer> list = new ArrayList<Integer>(); for (int i = -2; i < 3; i++) { set.add(i); list.add(i); } for (int i = 0; i < 3; i++) { set.remove(i); list.remove(i); } System.out.println(set + " " + list); } }

Birçoğumuz yukarıdaki kod parçasının çıktısının aşağıdaki gibi olacağını düşünebiliriz:
[-2, -1] [-2, -1]
Ama maalesef kod düşündüğümüzden farklı bir şekilde çalışarak, aşağıdaki çıktıyı üretecektir:
[-2, -1] [-1, 1]
Yukarıdaki durumun, Java 1.5 ile hayatımıza giren “autoboxing” özelliği ile “overloading” kavramlarının dikkatsiz kullanımından kaynaklandığını söyleyebiliriz.
Yukarıdaki koddaki “set.remove(i)” satırı ile, “remove(E)” methodu çağrılmaktadır. Buradaki “E”, set’in element tipi olan “Integer”’dır ve int değeri auotoboxing ile Integer değere çevrilir. Bu da beklediğimiz bir durumdur, yani kod set’in içindeki pozitif değerleri set’ten çıkarır.
Diğer taraftan, koddaki “list.remove(i)” satırı, overload edilen “remove(int i)” methodunu çağırır. Bu method ise, listedeki belirtilen posizyondaki değeri siler. Yani yukarıdaki kod, "list” ’ in içeriğinin [ -2, -1, 0, 1, 2 ] olduğu durumda, önce “0” pozisyonundaki, sonra “1” ve sonra da “2” poziyonundaki değeri listeden çıkarır. Sonunda listenin içeriği [-1, 1] olarak kalır. Buradaki karışıklığı önlemek için, “list.remove(i)” satırındaki i’yi Integer’a cast ederek doğru overload edilmiş methodun çağrılmasını sağlayabiliriz.

for (int i = 0; i < 3; i++) { set.remove(i); list.remove((Integer)i); // veya list.remove(Integer.valueOf(i)); }


Bu düzeltmeden sonra, kodumuz beklediğimiz gibi aşağıdaki çıktıyı verebilir:
[-2, -1] [-2, -1]
Yukarıdaki karışıklığın nedeni, compiler’ın List interface’inin overload edilen iki adet remove methodunu(remove(E) ve remove(int)) autoboxing özelliği nedeniyle karıştırmış olmasından kaynaklanmaktadır. Java 1.5 sonrası, generic ve autoboxing kavramları, List interface’ini bozdu diyebiliriz.
Buradan çıkaracağımız dersleri aşağıdaki gibi özetleyebiliriz:

  • Java class’larımızdaki method’larımızı overload edebiliyor olmamız, onları overload etmemiz gerektiği anlamına gelmemektedir.

  • Genel olarak, aynı sayıda parametre içeren methodlarımızı overload etmekten kaçınmamız, yani methodlarımız için anlamlı ve farklı isimler kullanmamız gerekmektedir. İlla bu şekilde yapmamız gerekiyorsa da, methodların aynı parametrelerde, aynı davranışı göstermelerine dikkat etmemiz gerekmektedir.

Yukarıdaki maddelere dikkat etmediğimiz durumda, methodları kullanacak kişilere çok dikkatli olmaları gerektiğini anlatmamız gerekecektir. Aksi halde methodların neden çalışmadıklarını anlamakta zorlanacaklardır.

Yararlandığım Kaynaklar
Efective Java 2nd Edition, J. Bloch

ali kemal taşçı

7 Ekim 2015 Çarşamba

Java Performans Notları: Autoboxing / Unboxing

0 yorum
English version is at DZone

Aşağıdaki kod parçasında sadece 1 karakteri değiştirerek, işlem süresini yaklaşık 5’te 1’ine indirebileceğimizi söylesem ne düşünürsünüz?

long t = System.currentTimeMillis(); Long sum = 0L; for (long i = 0; i < Integer.MAX_VALUE; i++) { sum += i; } System.out.println("toplam:" + sum); System.out.println("işlem süresi: " + (System.currentTimeMillis() - t) + " ms") ;

Çıktı:
toplam:2305843005992468481
işlem süresi:  6756 ms



Yukarıdaki kod parçası hakkında biraz düşündükten sonra, aşağıdaki daha hızlı çalışan kod parçasını inceleyebilirsiniz.

long t = System.currentTimeMillis(); //Long sum = 0L; long sum = 0L; for (long i = 0; i < Integer.MAX_VALUE; i++) { sum += i; } System.out.println("toplam:" + sum); System.out.println("işlem süresi: " + (System.currentTimeMillis() - t) + " ms") ;

Çıktı:
toplam:2305843005992468481
işlem süresi: 1248 ms

Yukarıdaki hız farkının, Java 1.5 ile hayatımıza giren “Autoboxing” özelliğinin dikkatsiz kullanımından kaynaklandığını söyleyebiliriz.

Farkın nedenine devam etmeden, öncelikle Java’da “Autoboxing” ve “Unboxing” kavramını açıklamaya çalışalım. 

Java’da tipler primitive ve referans olmak üzere ikiye ayrılmaktadır. Java’da 8 adet primitive tip bulunmaktadır. Her primitive tipe karşılık gelen de bir referans tipi(wrapper class) vardır. 

Primitive tipler
Referans Tipler(Wrapper Class)
boolean
Boolean
byte
Byte
char
Character
float
Float
int
Integer
long
Long
short
Short
double
Double

Aşağıdaki örnek kod parçasında autoboxing ve unboxing kavramlarına birer örnek görebilirsiniz. Burada Long bekleyen bir ArrayList’e long bir değer eklenmektedir. Java 1.4’te bu işlemin yapılabilmesi için primitive değerin uygun bir referans tip içine eklenerek(boxing) yapılması gerekirdi. Java 1.5 itibariyle bu işlemi compiler bizim için yapmaktadır. Böylece fazladan kod yazmak zorunda kalmıyoruz.

Yani aşağıdaki kod;

List<Long> longList = new ArrayList<>(); long i = 4; longList.add( i ); //autoboxing long j = longList.get( 0 ); //unboxing

compiler tarafından otomatik olarak aşağıdaki koda çevrilmektedir.

List<Long> longList = new ArrayList<>(); long i = 4; longList.add(Long.valueOf( i ) ); long j = longList.get( 0 ).longValue();

Buradan ilk kodumuzun da aslında aşağıdaki gibi bir koda çevrildiğini söyleyebiliriz. Bu işlemin uzun sürmesinin nedenini de kod çalışırken, 2147483647 adet gereksiz Long instance’ı oluşturmaya çalışması olarak açıklayabiliriz.

long t = System.currentTimeMillis(); Long sum = 0L; for (long i = 0; i < Integer.MAX_VALUE; i++) { sum += new Long(i); } System.out.println("toplam:" + sum); System.out.println("işlem süresi: " + (System.currentTimeMillis() - t) + " ms") ;

Çıktı:
toplam:2305843005992468481
işlem süresi: 6764 ms

Sonuç olarak, daha hızlı çalışacak Java kodları yazmak için, autoboxing ve unboxing durumlarını dikkate almalıyız. Gereksiz instance oluşturma işlemlerinden kaçınmalı, hesap gerektiren çoklu işlemlerde primitive tipleri tercih etmeliyiz diyebiliriz.

Yararlandığım Kaynaklar
Autoboxingand Unboxing
Autoboxing


ali kemal taşçı

5 Mart 2014 Çarşamba

Thread-Safe Olmayan SimpleDateFormat İçin Bir Çözüm : ThreadLocal

6 yorum
Bir uygulamamızda, standart tarih formatı oluşturmak için, aşağıdaki gibi bir BaseService class’ımızın içinde static date formatter’lar oluşturmuştuk:

public class BaseService { protected static final SimpleDateFormat SDF_DT = new SimpleDateFormat("dd.MM.yyyy HH:mm"); protected static final SimpleDateFormat SDF_D = new SimpleDateFormat("dd.MM.yyyy"); …

Daha sonra, bu formatter’ları, implement eden her class içinde aşağıdaki gibi kullanıyorduk:
public class CampaignRegistrationService extends BaseService{ … oblg.setExpireDate((SDF_D.format(expireDate))); … time = SDF_D.parse(iaArchivedCriteria.getValue()).getTime(); …

Uygulamamızda her zaman değil ama bazen, yoğun işlemler sırasında, formatter’larımızı kullandığımız yerlerden aşağıdaki gibi çeşitli hataların atıldığını gördük.

java.lang.NumberFormatException: For input string: "" at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48) at java.lang.Long.parseLong(Long.java:431) at java.lang.Long.parseLong(Long.java:468) at java.text.DigitList.getLong(DigitList.java:177) at java.text.DecimalFormat.parse(DecimalFormat.java:1297) at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1589) at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1311) at java.text.DateFormat.parse(DateFormat.java:335) at ...

java.lang.NumberFormatException: multiple points at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1082) at java.lang.Double.parseDouble(Double.java:510) at java.text.DigitList.getDouble(DigitList.java:151) at java.text.DecimalFormat.parse(DecimalFormat.java:1302) at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1934) at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1311) at java.text.DateFormat.parse(DateFormat.java:335) at ...

java.lang.ArrayIndexOutOfBoundsException: -1 at java.text.DigitList.fitsIntoLong(DigitList.java:212) at java.text.DecimalFormat.parse(DecimalFormat.java:1295) at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1934) at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1311) at java.text.DateFormat.parse(DateFormat.java:335) at ...


Her defasında farklı farklı ve koda bakınca da pek anlamlı gelmeyen bu hatalarla ilgili araştırma yaptığımızda, sorunun thread-safe olmayan, yani farklı threadler tarafından ortak kullanılan alanlara sahip olan java.text.SimpleDateFormat class’ının kullanımından kaynaklandığını farkettik.

Sorunu daha detaylı açıklayacak olursak, SimpleDateFormat class'ı methodlardaki hesaplamalarında kullandığı ara değerleri instance alanlarda saklamaktadır. Yani bir instance iki thread tarafından kullanıldığında,  her biri birbirinin sonucunu bozabilmektedir. Örneğin, java.text.DateFormat’ın kaynak koduna bakacak olursak, DateFormat veya SimpleDateFormat içindeki işlemlerde kullanılmak üzere bir Calendar instance alanı olduğunu görürüz.

public abstract class DateFormat extends Format { /** * The calendar that <code>DateFormat</code> uses to produce the time field * values needed to implement date and time formatting. Subclasses should * initialize this to a calendar appropriate for the locale associated with * this <code>DateFormat</code>. * @serial */ protected Calendar calendar; …
DateFormat ‘ı extend eden SimpleDateFormat’ın içindeki parse() methoduna bakacak olursak, ilk olarak, calendar.clear() methodunun çalıştırıldığını daha sonra da calendar.add(..) methodunun çalıştırıldığını görürüz. Eğer bir thread’in parse() method çağrımı tamamlanmadan, bir başka thread de parse() methodunu çağırırsa, ilk thread’in beklediği değerin, ikinci thread tarafından silinmesi(clean) durumu ortaya çıkacaktır.

public class SimpleDateFormat extends DateFormat { … public Date parse(String text, ParsePosition pos) { … calendar.clear(); // Clears all the time fields … Date parsedDate = calendar.getTime(); if( ambiguousYear[0] && !parsedDate.after(defaultCenturyStart) ) { calendar.add(Calendar.YEAR, 100); parsedDate = calendar.getTime(); } …

Bu durum da bizim bazen doğru sonuçlar alırken, bazen de alakasız hatalar almamız durumunu açıklıyor.

Burada yaşadığımız problemi ThreadLocal ile çözdük. Java 2’den beri var olan ThreadLocal sınıfı thread-local değişkenleri sağlar. Bu değişkenler, her thread’e özel olarak tutulur. Farklı thread’lar birbirlerinin thread-local değişkenlerine erişemez.

ThreadLocal  içerisinde get, set, initialValue ve remove metotları bulunur.
get() : ThreadLocal değişkeninin değerini döndürür.
set(T value) : ThreadLocal değişkeninin değerini günceller.
initialValue() : ThreadLocal değişkeninin ilk değerini döndürür.
remove() : ThreadLocal değişkeninin değerini siler. (Java 5 ile eklendi)

ThreadLocal ile her thread’ın kendi formatter’ını kullandığı çözümümüzü aşağıda görebilirsiniz:

import java.text.SimpleDateFormat; /* * Thread Safe implementation of SimpleDateFormat * Each Thread will get its own instance of SimpleDateFormat which will not be shared between other threads. * */ public class PerThreadFormatter { private static final ThreadLocal<SimpleDateFormat> dateFormatHolder = new ThreadLocal<SimpleDateFormat>() { /* * initialValue() is called */ @Override protected SimpleDateFormat initialValue() { return new SimpleDateFormat("dd.MM.yyyy"); } }; private static final ThreadLocal<SimpleDateFormat> dateTimeFormatHolder = new ThreadLocal<SimpleDateFormat>() { /* * initialValue() is called */ @Override protected SimpleDateFormat initialValue() { return new SimpleDateFormat("dd.MM.yyyy HH:mm"); } }; /* * Every time there is a call for DateTimeFormat, ThreadLocal will return calling * Thread's copy of SimpleDateFormat */ public static SimpleDateFormat getDateTimeFormatter() { return dateTimeFormatHolder.get(); } /* * Every time there is a call for DateFormat, ThreadLocal will return calling * Thread's copy of SimpleDateFormat */ public static SimpleDateFormat getDateFormatter() { return dateFormatHolder.get(); } }

import java.text.SimpleDateFormat; public class BaseService { protected static SimpleDateFormat getSDF_DT() { return PerThreadFormatter.getDateTimeFormatter(); } protected static SimpleDateFormat getSDF_D() { return PerThreadFormatter.getDateFormatter(); } …

public class CampaignRegistrationService extends BaseService{ … oblg.setExpireDate((getSDF_D().format(expireDate))); … time = getSDF_D().parse(iaArchivedCriteria.getValue()).getTime(); …

Yararlandığım Kaynaklar:
DateFormat in a Multithreading Environment
ThreadLocal in Java - Example Program and Tutorial
SimpleDateFormat thread safety
Multiple exceptions thrown parsing date string


ali kemal taşçı

8 Temmuz 2013 Pazartesi

IT eBooks

0 yorum
Aşağıdaki siteden, bilişim sektörüne yönelik, faydalı bulduğunuz, birçok e-kitabı indirebilirsiniz:

















 ali kemal taşçı

22 Mayıs 2011 Pazar

Java Thread - ExecutorService Örneği - Seri / Paralel

0 yorum
Aşağıda, Java 1.5 ile birlikte gelen ExecutorService arayüzünü kullanarak oluşturduğum bir multithread uygulama örneğini paylaşmak isterim. Bu örnekte, işlemlerin paralel ve seri olarak çalıştırılması ve sonuçlarının dönülmesini görebilirsiniz:

package karalama.thread;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

class TaskResult {
private String resultStr;
public TaskResult(String resultStr) {
this.resultStr = resultStr;
}
public String getResultStr() {
return resultStr;
}

}

public class ExecuterTester {

private static final class CallableTask implements Callable<TaskResult> {
private String name ;
private long waitTime;

public CallableTask(String name, long waitTime) {
this.name = name;
this.waitTime = waitTime;
}

@Override
public TaskResult call() throws Exception {
System.out.println(name
+ " started");
long t = System.currentTimeMillis();
Thread.sleep(waitTime);
String result
= name + " ended - " + (System.currentTimeMillis()-t) / 1000 + " sn geçti.";
//System.out.println(result);
return new TaskResult(result);
}
}

public static void main(String args[]) {
ExecutorService executorService
= Executors.newCachedThreadPool();
List
<Callable<TaskResult>> tasks = new ArrayList<Callable<TaskResult>>();

tasks.add(
new CallableTask("task1", 1000));
tasks.add(
new CallableTask("task2", 2000));
tasks.add(
new CallableTask("task3", 3000));
tasks.add(
new CallableTask("task4", 4000));
tasks.add(
new CallableTask("task5", 5000));

try {
System.out.println(
"Paralel İşlem");
long t = System.currentTimeMillis();
List
<Future<TaskResult>> futureList = executorService.invokeAll(tasks);
for (Future<TaskResult> future : futureList) {
TaskResult taskResult
= future.get();
System.out.println(taskResult.getResultStr());
}
System.out.println(
"Toplamda " + (System.currentTimeMillis()-t) / 1000 + " sn geçti.");
}
catch (InterruptedException e) {
e.printStackTrace();
}
catch (ExecutionException e) {
e.printStackTrace();
}

System.out.println(
"\nSeri İşlem");
long t = System.currentTimeMillis();
for (Callable<TaskResult> task : tasks) {
Future
<TaskResult> future = executorService.submit(task);
try {
TaskResult taskResult
= future.get();
System.out.println(taskResult.getResultStr());
}
catch (InterruptedException e) {
e.printStackTrace();
}
catch (ExecutionException e) {
e.printStackTrace();
}

}
System.out.println(
"Toplamda " + (System.currentTimeMillis()-t) / 1000 + " sn geçti.");
}



}


Uygulama Çıktısı:

Paralel İşlem
task1 başladı
task2 başladı
task3 başladı
task4 başladı
task5 başladı
task1 bitti - 1 sn geçti.
task2 bitti - 2 sn geçti.
task3 bitti - 3 sn geçti.
task4 bitti - 4 sn geçti.
task5 bitti - 5 sn geçti.
Toplamda 5 sn geçti.

Seri İşlem
task1 başladı
task1 bitti - 1 sn geçti.
task2 başladı
task2 bitti - 2 sn geçti.
task3 başladı
task3 bitti - 3 sn geçti.
task4 başladı
task4 bitti - 4 sn geçti.
task5 başladı
task5 bitti - 5 sn geçti.
Toplamda 15 sn geçti.


ali kemal taşçı