Home » Java » Java MultiThreading (2): Konsep Dasar dan Source Code

Java MultiThreading (2): Konsep Dasar dan Source Code

by Bagus Dharma Iswara
by Bagus Dharma Iswara

Saat melakukan pemrograman Multithreading di Java, Anda harus memiliki konsep berikut yang sangat berguna.

  1. Thread Synchronization.
  2. Interthread Communication.
  3. Thread Deadlock.
  4. Thread Control

Thread Synchronization

Ketika kita memulai dua atau lebih utas dalam sebuah program, mungkin ada situasi ketika beberapa utas mencoba mengakses sumber daya yang sama dan akhirnya mereka dapat menghasilkan hasil yang tidak terduga karena masalah konkurensi. Misalnya, jika beberapa utas mencoba untuk menulis dalam file yang sama maka mereka dapat merusak data karena salah satu utas dapat menimpa data atau saat satu utas membuka file yang sama pada saat yang sama utas lain mungkin menutup file yang sama.

Jadi, ada kebutuhan untuk menyinkronkan tindakan beberapa utas dan memastikan bahwa hanya satu utas yang dapat mengakses sumber daya pada titik waktu tertentu. Ini diimplementasikan dengan menggunakan konsep yang disebut monitor. Setiap objek di Java dikaitkan dengan monitor, yang dapat dikunci atau dibuka kuncinya oleh thread. Hanya satu utas pada satu waktu yang dapat menahan kunci pada monitor.

Bahasa pemrograman Java menyediakan cara yang sangat berguna untuk membuat utas dan menyinkronkan tugas mereka dengan menggunakan blok tersinkronisasi. Anda menyimpan sumber daya bersama dalam blok ini. Berikut adalah bentuk umum dari pernyataan tersinkronisasi.

Sintaks pada thread sinkronisasi ini adalah

synchronized(objectidentifier) {
   // Access shared variables and other shared resources
}

Di sini, pengidentifikasi objek adalah referensi ke objek yang kuncinya terkait dengan monitor yang diwakili oleh pernyataan tersinkronisasi. Sekarang kita akan melihat dua contoh, di mana kita akan mencetak penghitung menggunakan dua utas berbeda. Ketika utas tidak disinkronkan, mereka mencetak nilai penghitung yang tidak berurutan, tetapi ketika kami mencetak penghitung dengan meletakkan di dalam blok synchronized (), kemudian mencetak penghitung sangat berurutan untuk kedua utas.

Multithreading Tidak Menggunakan Synchronization

Berikut adalah contoh sederhana yang mungkin atau mungkin tidak mencetak nilai counter secara berurutan dan setiap kali kita menjalankannya, itu menghasilkan hasil yang berbeda berdasarkan ketersediaan CPU untuk sebuah thread. Dapat melihat contoh kode dibawah untuk lebih jelasnya

class PrintDemo {
   public void printCount() {
      try {
         for(int i = 5; i > 0; i--) {
            System.out.println("Counter   ---   "  + i );
         }
      } catch (Exception e) {
         System.out.println("Thread  interrupted.");
      }
   }
}

class ThreadDemo extends Thread {
   private Thread t;
   private String threadName;
   PrintDemo  PD;

   ThreadDemo( String name,  PrintDemo pd) {
      threadName = name;
      PD = pd;
   }
   
   public void run() {
      PD.printCount();
      System.out.println("Thread " +  threadName + " exiting.");
   }

   public void start () {
      System.out.println("Starting " +  threadName );
      if (t == null) {
         t = new Thread (this, threadName);
         t.start ();
      }
   }
}

public class TestThread {
   public static void main(String args[]) {

      PrintDemo PD = new PrintDemo();

      ThreadDemo T1 = new ThreadDemo( "Thread - 1 ", PD );
      ThreadDemo T2 = new ThreadDemo( "Thread - 2 ", PD );

      T1.start();
      T2.start();

      // wait for threads to end
         try {
         T1.join();
         T2.join();
      } catch ( Exception e) {
         System.out.println("Interrupted");
      }
   }
}

Ini menghasilkan hasil yang berbeda setiap kali Anda menjalankan program ini yaitu

Starting Thread - 1
Starting Thread - 2
Counter   ---   5
Counter   ---   4
Counter   ---   3
Counter   ---   5
Counter   ---   2
Counter   ---   1
Counter   ---   4
Thread Thread - 1  exiting.
Counter   ---   3
Counter   ---   2
Counter   ---   1
Thread Thread - 2  exiting.

Multithreading Menggunakan Synchronization

Berikut adalah contoh yang sama yang mencetak nilai counter secara berurutan dan setiap kali kita menjalankannya, menghasilkan hasil yang sama. Kita dapat melihat contoh kode berikut

class PrintDemo {
   public void printCount() {
      try {
         for(int i = 5; i > 0; i--) {
            System.out.println("Counter   ---   "  + i );
         }
      } catch (Exception e) {
         System.out.println("Thread  interrupted.");
      }
   }
}

class ThreadDemo extends Thread {
   private Thread t;
   private String threadName;
   PrintDemo  PD;

   ThreadDemo( String name,  PrintDemo pd) {
      threadName = name;
      PD = pd;
   }
   
   public void run() {
      synchronized(PD) {
         PD.printCount();
      }
      System.out.println("Thread " +  threadName + " exiting.");
   }

   public void start () {
      System.out.println("Starting " +  threadName );
      if (t == null) {
         t = new Thread (this, threadName);
         t.start ();
      }
   }
}

public class TestThread {

   public static void main(String args[]) {
      PrintDemo PD = new PrintDemo();

      ThreadDemo T1 = new ThreadDemo( "Thread - 1 ", PD );
      ThreadDemo T2 = new ThreadDemo( "Thread - 2 ", PD );

      T1.start();
      T2.start();

      // wait for threads to end
      try {
         T1.join();
         T2.join();
      } catch ( Exception e) {
         System.out.println("Interrupted");
      }
   }
}

Ini menghasilkan hasil yang sama setiap kali Anda menjalankan program ini yaitu

Starting Thread - 1
Starting Thread - 2
Counter   ---   5
Counter   ---   4
Counter   ---   3
Counter   ---   2
Counter   ---   1
Thread Thread - 1  exiting.
Counter   ---   5
Counter   ---   4
Counter   ---   3
Counter   ---   2
Counter   ---   1
Thread Thread - 2  exiting.

Interthread Communication

Jika Anda mengetahui komunikasi antarproses maka akan mudah bagi Anda untuk memahami komunikasi interthread. Komunikasi interthread penting ketika Anda mengembangkan aplikasi di mana dua atau lebih utas bertukar beberapa informasi.

Ada tiga metode sederhana dan sedikit trik yang memungkinkan komunikasi utas. Ketiga metode tersebut tercantum di bawah ini yaitu

  • public void wait () yaitu Menyebabkan utas saat ini menunggu hingga thread lain untuk memanggil notify ().
  • public void notify () yaitu Membangunkan satu thread yang menunggu di monitor pada objek ini.
  • public void notifyAll () yaitu Membangunkan semua thread yang memanggil wait () pada objek yang sama.

Metode ini telah diimplementasikan sebagai metode terakhir di Object, sehingga tersedia di semua kelas. Ketiga metode hanya dapat dipanggil dari dalam konteks yang disinkronkan. Contoh ini menunjukkan bagaimana dua thread dapat berkomunikasi menggunakan metode wait () dan notify (). Anda dapat membuat sistem yang kompleks dengan menggunakan konsep yang sama. Contohnya dapat dilihat pada kode berikut

class Chat {
   boolean flag = false;

   public synchronized void Question(String msg) {
      if (flag) {
         try {
            wait();
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
      System.out.println(msg);
      flag = true;
      notify();
   }

   public synchronized void Answer(String msg) {
      if (!flag) {
         try {
            wait();
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }

      System.out.println(msg);
      flag = false;
      notify();
   }
}

class T1 implements Runnable {
   Chat m;
   String[] s1 = { "Hi", "How are you ?", "I am also doing fine!" };

   public T1(Chat m1) {
      this.m = m1;
      new Thread(this, "Question").start();
   }

   public void run() {
      for (int i = 0; i < s1.length; i++) {
         m.Question(s1[i]);
      }
   }
}

class T2 implements Runnable {
   Chat m;
   String[] s2 = { "Hi", "I am good, what about you?", "Great!" };

   public T2(Chat m2) {
      this.m = m2;
      new Thread(this, "Answer").start();
   }

   public void run() {
      for (int i = 0; i < s2.length; i++) {
         m.Answer(s2[i]);
      }
   }
}
public class TestThread {
   public static void main(String[] args) {
      Chat m = new Chat();
      new T1(m);
      new T2(m);
   }
}

Ketika program di atas dipenuhi dan dijalankan, itu menghasilkan hasil sebagai berikut

Hi
Hi
How are you ?
I am good, what about you?
I am also doing fine!
Great!

Thread Deadlock

Deadlock menggambarkan situasi di mana dua atau lebih utas diblokir selamanya, menunggu satu sama lain. Kebuntuan terjadi ketika beberapa utas membutuhkan kunci yang sama tetapi mendapatkannya dalam urutan yang berbeda. Program multithread Java mungkin mengalami kondisi kebuntuan karena kata kunci tersinkronisasi menyebabkan thread yang menjalankan memblokir saat menunggu kunci, atau monitor, yang terkait dengan objek tertentu. Berikut ini contohnya dari thread deadlock pada java

public class TestThread {
   public static Object Lock1 = new Object();
   public static Object Lock2 = new Object();
   
   public static void main(String args[]) {
      ThreadDemo1 T1 = new ThreadDemo1();
      ThreadDemo2 T2 = new ThreadDemo2();
      T1.start();
      T2.start();
   }
   
   private static class ThreadDemo1 extends Thread {
      public void run() {
         synchronized (Lock1) {
            System.out.println("Thread 1: Holding lock 1...");
            
            try { Thread.sleep(10); }
            catch (InterruptedException e) {}
            System.out.println("Thread 1: Waiting for lock 2...");
            
            synchronized (Lock2) {
               System.out.println("Thread 1: Holding lock 1 & 2...");
            }
         }
      }
   }
   private static class ThreadDemo2 extends Thread {
      public void run() {
         synchronized (Lock2) {
            System.out.println("Thread 2: Holding lock 2...");
            
            try { Thread.sleep(10); }
            catch (InterruptedException e) {}
            System.out.println("Thread 2: Waiting for lock 1...");
            
            synchronized (Lock1) {
               System.out.println("Thread 2: Holding lock 1 & 2...");
            }
         }
      }
   } 
}

Ketika Anda mengkompilasi dan menjalankan program di atas, Anda menemukan situasi kebuntuan dan berikut adalah keluaran yang dihasilkan oleh program

Thread 1: Holding lock 1...
Thread 2: Holding lock 2...
Thread 1: Waiting for lock 2...
Thread 2: Waiting for lock 1...

Program di atas akan hang selamanya karena tak satu pun dari utas dalam posisi untuk melanjutkan dan menunggu satu sama lain untuk melepaskan kunci, sehingga Anda dapat keluar dari program dengan menekan CTRL + C.

Solusi untuk Thread Deadlock

Mari kita ubah urutan kunci dan menjalankan program yang sama untuk melihat apakah kedua utas masih menunggu satu sama lain. Berikut merupakan contoh kode yang dapat digunakan

public class TestThread {
   public static Object Lock1 = new Object();
   public static Object Lock2 = new Object();
   
   public static void main(String args[]) {
      ThreadDemo1 T1 = new ThreadDemo1();
      ThreadDemo2 T2 = new ThreadDemo2();
      T1.start();
      T2.start();
   }
   
   private static class ThreadDemo1 extends Thread {
      public void run() {
         synchronized (Lock1) {
            System.out.println("Thread 1: Holding lock 1...");
            
            try {
               Thread.sleep(10);
            } catch (InterruptedException e) {}
            System.out.println("Thread 1: Waiting for lock 2...");
            
            synchronized (Lock2) {
               System.out.println("Thread 1: Holding lock 1 & 2...");
            }
         }
      }
   }
   private static class ThreadDemo2 extends Thread {
      public void run() {
         synchronized (Lock1) {
            System.out.println("Thread 2: Holding lock 1...");
           
            try {
               Thread.sleep(10);
            } catch (InterruptedException e) {}
            System.out.println("Thread 2: Waiting for lock 2...");
            
            synchronized (Lock2) {
               System.out.println("Thread 2: Holding lock 1 & 2...");
            }
         }
      }
   } 
}

Jadi hanya mengubah urutan kunci mencegah program mengalami situasi kebuntuan dan selesai dengan hasil sebagai berikut

Thread 1: Holding lock 1...
Thread 1: Waiting for lock 2...
Thread 1: Holding lock 1 & 2...
Thread 2: Holding lock 1...
Thread 2: Waiting for lock 2...
Thread 2: Holding lock 1 & 2...

Contoh di atas adalah untuk memperjelas konsepnya, namun, ini adalah konsep yang kompleks dan Anda harus menyelami lebih dalam sebelum Anda mengembangkan aplikasi untuk menghadapi situasi kebuntuan.

Thread Control

Core Java menyediakan kendali penuh atas program multithread. Anda dapat mengembangkan program multithread yang dapat ditangguhkan, dilanjutkan, atau dihentikan sepenuhnya berdasarkan kebutuhan Anda. Ada berbagai metode statis yang dapat Anda gunakan pada objek thread untuk mengontrol perilakunya. Berikut ini mencantumkan metode tersebut yaitu

  • public void suspend () yaitu Metode ini menempatkan utas dalam status ditangguhkan dan dapat dilanjutkan menggunakan metode resume ().
  • public void stop () yaitu Metode ini menghentikan utas sepenuhnya.
  • resume public void () yaitu Metode ini melanjutkan utas, yang ditangguhkan menggunakan metode suspend ().
  • public void wait () yaitu Menyebabkan utas saat ini menunggu hingga utas lain memanggil metode notify ().
  • public void notify () yaitu Membangunkan satu utas yang menunggu di monitor objek ini.

Ketahuilah bahwa versi terbaru Java tidak lagi menggunakan metode suspend (), resume (), dan stop () sehingga Anda perlu menggunakan alternatif yang tersedia. Berikut ini kita dapat membuat thread control dengan menuliskan kode berikut

class RunnableDemo implements Runnable {
   public Thread t;
   private String threadName;
   boolean suspended = false;

   RunnableDemo( String name) {
      threadName = name;
      System.out.println("Creating " +  threadName );
   }
   
   public void run() {
      System.out.println("Running " +  threadName );
      try {
         for(int i = 10; i > 0; i--) {
            System.out.println("Thread: " + threadName + ", " + i);
            // Let the thread sleep for a while.
            Thread.sleep(300);
            synchronized(this) {
               while(suspended) {
                  wait();
               }
            }
         }
      } catch (InterruptedException e) {
         System.out.println("Thread " +  threadName + " interrupted.");
      }
      System.out.println("Thread " +  threadName + " exiting.");
   }

   public void start () {
      System.out.println("Starting " +  threadName );
      if (t == null) {
         t = new Thread (this, threadName);
         t.start ();
      }
   }
   
   void suspend() {
      suspended = true;
   }
   
   synchronized void resume() {
      suspended = false;
      notify();
   }
}

public class TestThread {

   public static void main(String args[]) {

      RunnableDemo R1 = new RunnableDemo( "Thread-1");
      R1.start();

      RunnableDemo R2 = new RunnableDemo( "Thread-2");
      R2.start();

      try {
         Thread.sleep(1000);
         R1.suspend();
         System.out.println("Suspending First Thread");
         Thread.sleep(1000);
         R1.resume();
         System.out.println("Resuming First Thread");
         
         R2.suspend();
         System.out.println("Suspending thread Two");
         Thread.sleep(1000);
         R2.resume();
         System.out.println("Resuming thread Two");
      } catch (InterruptedException e) {
         System.out.println("Main thread Interrupted");
      }try {
         System.out.println("Waiting for threads to finish.");
         R1.t.join();
         R2.t.join();
      } catch (InterruptedException e) {
         System.out.println("Main thread Interrupted");
      }
      System.out.println("Main thread exiting.");
   }
}

Program di atas menghasilkan keluaran sebagai berikut

Creating Thread-1
Starting Thread-1
Creating Thread-2
Starting Thread-2
Running Thread-1
Thread: Thread-1, 10
Running Thread-2
Thread: Thread-2, 10
Thread: Thread-1, 9
Thread: Thread-2, 9
Thread: Thread-1, 8
Thread: Thread-2, 8
Thread: Thread-1, 7
Thread: Thread-2, 7
Suspending First Thread
Thread: Thread-2, 6
Thread: Thread-2, 5
Thread: Thread-2, 4
Resuming First Thread
Suspending thread Two
Thread: Thread-1, 6
Thread: Thread-1, 5
Thread: Thread-1, 4
Thread: Thread-1, 3
Resuming thread Two
Thread: Thread-2, 3
Waiting for threads to finish.
Thread: Thread-1, 2
Thread: Thread-2, 2
Thread: Thread-1, 1
Thread: Thread-2, 1
Thread Thread-1 exiting.
Thread Thread-2 exiting.
Main thread exiting.

You may also like