Sunday, March 25, 2018

11. Pemrograman Multithread Bagian Satu



Java menyediakan dukungan pustaka untuk pemrograman multithread. Program multithread memuat dua atau lebih bagian program yang berjalan bersamaan. Setiap bagian program tersebut dinamakan dengan thread, dan setiap thread mendefinisikan jalur eksekusi program yang terpisah. Jadi, pemrograman multithread merupakan salah satu bentuk multitasking.

Anda tentu sudah familiar dengan multitasking karena hal itu didukung oleh semua sistem operasi modern. Namun, ada dua jenis berbeda dari multitasking: berbasis proses dan berbasis thread. Penting untuk mengetahui perbedaan dari keduanya. Bagi banyak pembaca, multitasking berbasis proses merupakan bentuk yang paling familiar. Proses adalah, pada dasarnya, sebuah program yang dieksekusi. Jadi, multitasking berbasis proses adalah fitur yang bisa digunakan oleh komputer Anda untuk menjalankan dua atau lebih program secara bersamaan. Misalnya, multitasking berbasis proses dapat dipakai untuk menjalankan kompilator Java pada saat yang sama seperti ketika Anda menggunakan editor teks atau ketika Anda melihat situs Web. 


Kelas Thread dan Antarmuka Runnable
Sistem multithread pada Java dibangun di atas kelas Thread, metode-metode pada kelas tersebut, dan antarmuka Runnable. Kelas Thread mengenkapsulasi sebuah thread eksekusi. Untuk menciptakan sebuah thread baru, program Anda perlu mewarisi kelas Thread atau mengimplementasikan antarmuka Runnable.

Kelas Thread mendefinisikan sejumlah metode yang membantu mengelola thread. Beberapa di antaranya yang akan digunakan pada bab ini ditunjukkan berikut:

Metode
Arti
getName
Mendapatkan nama dari sebuah thread.
getPriority
Mendapatkan prioritas dari thread.
isAlive
Menentukan apakah sebuah thread masih berjalan atau tidak.
join
Menunggu sebuah thread untuk berhenti.
run
Titik masuk bagi thread
sleep
Menunda sebuah thread untuk periode waktu tertentu.
start
Memulai sebuah thread dengan memanggil metode run().


Thread Utama
Ketika sebuah program Java mulai dijalankan, satu thread akan dijalankan segera. Ini umumnya dikenal sebagai thread utama dari program Anda, karena ia akan dieksekusi ketika program Anda dimulai. Thread utama itu penting karena dua alasan:

  • Merupakan thread dari mana thread-thread “anak” akan dihasilkan.
  • Biasanya merupakan thread terakhir untuk menuntaskan eksekusi karena thread utama ini melakukan sejumlah pekerjaan shutdown.


Meskipun thread utama diciptakan secara otomatis ketika program Anda dijalankan, ia dapat dikendalikan melalui sebuah objek Thread. Untuk melakukannya, Anda perlu mendapatkan sebuah referensi yang menunjuk objek itu dengan memanggil metode currentThread(), yang merupakan sebuah anggota public static dari kelas Thread. Bentuk umumnya ditampilkan di sini:

static Thread currentThread()

Metode ini menghasilkan sebuah referensi yang menunjuk ke thread di mana metode ini dipanggil. Setelah Anda memiliki referensi yang menunjuk ke thread utama, Anda bisa mengendalikannya sama seperti thread lainnya.

Perhatikan contoh berikut:

// Mengendalikan thread utama.
public class DemoCurrentThread {
   public static void main(String args[]) {
      Thread t = Thread.currentThread();
      System.out.println("Thread utama: " + t);
  
      // Mengubah nama dari thread
      t.setName("Thread Saya");
      System.out.println("Setelah nama berubah: " + t);
  
      try {
         for(int n = 5; n > 0; n--) {
            System.out.println(n);
            Thread.sleep(1000);
         }
      } catch (InterruptedException e) {
         System.out.println("Thread utama diinterupsi");
      }
   }
}

Pada program ini, sebuah referensi yang menunjuk ke thread terkini (pada kasus ini, thread utama) didapatkan dengan memanggil metode currentThread(), dan referensi ini disimpan ke dalam variabel lokal t. Selanjutnya, program menampilkan informasi tentang thread. Program kemudian memanggil setName() untuk mengubah nama internal dari thread. Informasi seputar thread kemudian ditampilkan ulang. Kemudian, sebuah loop mencacah mundur dari lima, menunda satu detik di antara tiap baris. Penundaan tersebut dilakukan oleh metode sleep(). Argumen pada sleep() menetapkan periode tunda dalam milidetik. Perhatikan bahwa blok try/catch mengapit loop ini. Metode sleep() pada kelas Thread bisa melemparkan eksepsi InterruptedException. Ini akan terjadi jika thread lain ingin menginterupsi pencacahan ini. Contoh ini hanya menampilkan sebuah pesan jika pencacahan mundur tersebut diinterupsi. Pada program di dunia nyata, Anda perlu menangani ini secara berbeda. Berikut adalah keluaran yang dibangkitkan oleh program ini:

Thread utama: Thread[main,5,main]
Setelah nama berubah: Thread[Thread Saya,5,main]
5
4
3
2
1

Perhatikan keluaran yang dihasilkan ketika t dipakai sebagai argumen kepada println(). Ini menampilkan, secara berurutan: nama thread, prioritasnya, dan nama grupnya. Secara default, nama dari thread utama adalah main. Prioritasnya adalah 5, yang merupakan nilai default, dan main juga merupakan nama dari grup thread dimana thread utama ini berada. Grup thread adalah sebuah struktur data yang mengendalikan status koleksi thread secara keseluruhan. Setelah nama thread diubah, t ditampilkan kembali. Kali ini, nama baru thread ditampilkan.

Lihat lebih dekat pada metode-metode yang didefinisikan oleh kelas Thread yang dipakai pada program. Metode sleep() menyebabkan thread, dari mana metode itu dipanggil, untuk menunda eksekusi selama periode milidetik yang ditetapkan. Bentuk umumnya ditunjukkan di sini:

static void sleep(long milidetik) throws InterruptedException

Jumlah milidetik untuk penundaan ditetapkan pada milidetik. Metode ini bisa melemparkan eksepsi InterruptedException.

Metode sleep() memiliki bentuk kedua, yang ditunjukkan berikut, yang dapat Anda pakai untuk memberika periode penundaan dalam milidetik dan nanodetik:

static void sleep(long milidetik, int nanodetik) throws InterruptedException

Bentuk kedua ini berguna pada situasi yang memerlukan periode penundaan dalam satuan nanodetik.

Seperti yang ditunjukkan pada program tersebut, Anda bisa memberikan nama thread menggunakan metode setName(). Anda dapat memperoleh nama thread dengan memanggil getName() (tetapi ini tidak dilakukan pada program). Metode-metode ini merupakan anggota dari kelas Thread dan dideklarasikan seperti ini:

final void setName(String namaThread)
final String getName()

Di sini, namaThread menetapkan nama dari thread.


Menciptakan Sebuah Thread
Anda bisa menciptakan sebuah thread dengan menginstansiasi (menciptakan objek) bertipe Thread. Java mendefinisikan dua cara dalam melakukannya:

  • Anda bisa mengimplementasikan antarmuka Runnable.
  • Anda bisa mewarisi kelas Thread itu sendiri.

Dua bagian berikut akan mempelajari tiap metode ini secara bergiliran.


Mengimplementasikan Antarmuka Runnable
Cara termudah dalam menciptakan sebuah thread adalah dengan menciptakan sebuah kelas yang mengimplementasikan antarmuka Runnable. Antarmuka ini memuat abstraksi dari kode yang dapat dieksekusi. Anda dapat menciptakan sebuah thread pada sembarang objek yang mengimplementasikan Runnable. Untuk mengimplementasikan Runnable, sebuah kelas hanya perlu mengimplementasikan metode run(), yang dideklarasikan seperti ini:

public void run()

Di dalam run(), Anda perlu mendefinisikan kode yang memuat thread baru. Penting dipahami bahwa run() dapat memanggil metode-metode lain, menggunakan kelas-kelas lain, dan mendeklarasikan variabel-variabel, sama seperti thread utama. Satu-satunya perbedaan adalah bahwa run() menetapkan titik masuk untuk thread lain yang akan dieksekusi bersamaan pada program Anda. Thread ini akan berakhir ketika run() selesai dieksekusi.

Setelah Anda menciptakan sebuah kelas yang mengimplementasikan Runnable, Anda perlu menginstansiasi sebuah objek bertipe Thread dari dalam kelas tersebut. Kelas Thread mendefinisikan beberapa konstruktor. Salah satu konstruktor yang akan digunakan di sini adalah:

Thread(Runnable objekThread, String namaThread)

Pada konstruktor ini, objekThread adalah sebuah objek dari kelas yang mengimplementasikan antarmuka Runnable. Ini mendefinisikan di mana eksekusi thread akan dimulai. Nama dari thread baru ditetapkan oleh namaThread.

Setelah thread baru diciptakan, ia tidak akan mulai berjalan sampai Anda memanggil metode start(), yang dideklarasikan di dalam kelas Thread. Pada intinya, start() dieksekusi untuk memanggil run(). Metode start() ditunjukkan di sini:

void start()

Berikut adalah sebuah contoh yang menciptakan sebuah thread baru dan bagaimana menjalankannya:

// Menciptakan thread kedua.
class ThreadBaru implements Runnable {
   Thread t;

   ThreadBaru() {
      // Menciptakan sebuah thread baru, kedua
      t = new Thread(this, "Demo Thread");
      System.out.println("Thread Anak: " + t);
      t.start(); // Memulai thread
   }

   // Ini merupakan titik masuk untuk thread kedua.
   public void run() {
      try {
         for(int i = 5; i > 0; i--) {
            System.out.println("Thread Anak: " + i);
            Thread.sleep(500);
         }
      } catch (InterruptedException e) {
         System.out.println("Thread Anak diinterupsi.");
      }
      System.out.println("Keluar dari Thread Anak.");
   }
}

public class DemoThread {
   public static void main(String args[ ] ) {
      new ThreadBaru(); // Menciptakan sebuah thread baru
  
      try {
         for(int i = 5; i > 0; i--) {
            System.out.println("Thread Utama: " + i);
            Thread.sleep(1000);
         }
      } catch (InterruptedException e) {
         System.out.println("Thread Utama diinterupsi.");
      }
  
      System.out.println("Keluar dari Thread Utama.");
   }
}

Di dalam konstruktor dari ThreadBaru, sebuah objek Thread baru diciptakan dengan statemen berikut:

t = new Thread(this, Demo Thread);

Pelewatan this sebagai argumen pertama mengindikasikan bahwa Anda menginginkan agar thread baru ini memanggil metode run() pada objek this. Selanjutnya, start() dipanggil, yang mengawali thread eksekusi yang diawali pada metode run(). Ini menyebabkan loop for pada thread anak dimulai. Setelah memanggil start(), konstruktor dari ThreadBaru kembali ke main(). Ketika thread utama dimulai, ia memasuki loop for-nya. Kedua thread terus berjalan secara bersamaan, berbagi-pakai CPU pada sistem inti-tunggal, sampai selesai. Keluaran yang dihasilkan oleh program ini (Keluaran pada program Anda bisa jadi berbeda karena spesifikasi pada situasi CPU yang Anda gunakan):

Thread Anak: Thread[Demo Thread,5,main]
Thread Utama: 5
Thread Anak: 5
Thread Anak: 4
Thread Anak: 3
Thread Utama: 4
Thread Anak: 2
Thread Anak: 1
Thread Utama: 3
Keluar dari Thread Anak.
Thread Utama: 2
Thread Utama: 1
Keluar dari Thread Utama.

Seperti disebutkan sebelumnya, pada sebuah program multithread, seringkali ditemui bahwa thread utama akan menjadi thread terakhir atau thread penutup.


Mewarisi Kelas Thread
Cara kedua dalam menciptakan sebuah thread adalah dengan menciptakan sebuah kelas yang mewarisi Thread, dan kemudian menciptakan objek dari kelas itu. Kelas pewaris harus mendefinisikan ulang metode run(), yang merupakan titik masuk bagi thread baru tersebut. Kelas ini juga perlu memanggil metode call() untuk memulai eksekusi terhadap thread baru. Berikut adalah program sebelumnya yang ditulis ulang agar mewarisi kelas Thread:

thread baru. Berikut adalah program sebelumnya yang ditulis ulang agar mewarisi kelas Thread:

// Menciptakan sebuah thread kedua dengan mewarisi kelas Thread
class ThreadBaru extends Thread {
   ThreadBaru() {
      // Menciptakan thread kedua
      super("Demo Thread");
      System.out.println("Thread Anak: " + this);
      start(); // Memulai thread
   }

   // Ini merupakan titik masuk untuk thread kedua.
   public void run() {
      try {
         for(int i = 5; i > 0; i--) {
            System.out.println("Thread Anak: " + i);
            Thread.sleep(500);
         }
      } catch (InterruptedException e) {
         System.out.println("Thread Anak diinterupsi.");
      }

      System.out.println("Keluar dari Thread Anak.");
   }
}

public class MewarisiThread {
   public static void main(String args[]) {
      new ThreadBaru(); // menciptakan thread baru
  
      try {
         for(int i = 5; i > 0; i--) {
            System.out.println("Thread Utama: " + i);
            Thread.sleep(1000);
         }
      } catch (InterruptedException e) {
         System.out.println("Thread Utama diinterupsi.");
      }
  
      System.out.println("Keluar dari Thread Utama.");
   }
}

Jika dijalankan, program tersebut menghasilkan contoh keluaran berikut:

Thread Anak: Thread[Demo Thread,5,main]
Thread Utama: 5
Thread Anak: 5
Thread Anak: 4
Thread Anak: 3
Thread Utama: 4
Thread Anak: 2
Thread Utama: 3
Thread Anak: 1
Keluar dari Thread Anak.
Thread Utama: 2
Thread Utama: 1
Keluar dari Thread Utama.

Program ini menghasilkan keluaran sama seperti yang dihasilkan versi program sebelumnya. Seperti yang dapat Anda lihat, thread anak diciptakan dengan menginstansiasi sebuah objek dari ThreadBaru, yang diderivasi dari kelas Thread.

Perhatikan pemanggilan terhadap super() di dalam ThreadBaru. Ini akan memanggil bentuk berikut dari konstruktor Thread:

public Thread(String namaThread)

Di sini, namaThread menetapkan nama dari thread.


Selanjutnya  >>>


No comments: