Multi Threading #
Multithreading di Java adalah kemampuan untuk menjalankan beberapa alur eksekusi (thread) secara bersamaan dalam satu aplikasi. Hal ini memungkinkan program untuk melakukan beberapa tugas secara simultan, meningkatkan efisiensi dan performa terutama pada sistem dengan banyak prosesor. Java menyediakan berbagai cara untuk bekerja dengan thread, termasuk penggunaan kelas Thread
, antarmuka Runnable
, dan kerangka kerja Executor
.
Konsep Dasar #
- Thread: Unit terkecil dari eksekusi dalam Java. Setiap thread berjalan secara bersamaan dengan thread lainnya.
- Concurrency: Kemampuan untuk menjalankan beberapa thread secara bersamaan.
- Synchronization: Mekanisme untuk mengontrol akses thread ke sumber daya bersama agar tidak terjadi konflik.
Membuat dan Menjalankan Thread #
Ada dua cara utama untuk membuat thread di Java:
- Mengimplementasikan Antarmuka
Runnable
- Mengextends Kelas
Thread
Antarmuka Runnable
#
Antarmuka Runnable
harus diimplementasikan oleh kelas yang mendefinisikan metode run()
. Thread kemudian dapat dibuat dengan mempassing instance Runnable
ke konstruktor Thread
.
Contoh:
class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getId() + " Value: " + i);
try {
Thread.sleep(500); // Menunggu 500 ms
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class RunnableExample {
public static void main(String[] args) {
Thread thread1 = new Thread(new MyRunnable());
Thread thread2 = new Thread(new MyRunnable());
thread1.start(); // Memulai thread1
thread2.start(); // Memulai thread2
}
}
Kelas Thread
#
Anda dapat membuat thread dengan mengextends kelas Thread
dan override metode run()
.
Contoh:
class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getId() + " Value: " + i);
try {
Thread.sleep(500); // Menunggu 500 ms
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class ThreadExample {
public static void main(String[] args) {
Thread thread1 = new MyThread();
Thread thread2 = new MyThread();
thread1.start(); // Memulai thread1
thread2.start(); // Memulai thread2
}
}
Synchronization #
Jika beberapa thread mengakses sumber daya yang sama, Anda perlu memastikan bahwa hanya satu thread yang dapat mengakses sumber daya tersebut pada satu waktu untuk menghindari kondisi balapan (race conditions). Ini dapat dilakukan menggunakan kata kunci synchronized
.
Contoh:
class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public int getCount() {
return count;
}
}
public class SynchronizationExample {
public static void main(String[] args) {
Counter counter = new Counter();
Runnable task = () -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
};
Thread thread1 = new Thread(task);
Thread thread2 = new Thread(task);
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Final count: " + counter.getCount());
}
}
Executor Framework #
Java menyediakan kerangka kerja Executor
untuk mengelola dan menjalankan thread secara lebih efisien daripada membuat dan mengelola thread secara manual.
Menggunakan ExecutorService
#
ExecutorService
menyediakan cara untuk mengelola kumpulan thread dan menjalankan tugas-tugas asynchronous.
Contoh:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class ExecutorServiceExample {
public static void main(String[] args) {
// Membuat thread pool dengan 2 thread
ExecutorService executor = Executors.newFixedThreadPool(2);
Runnable task1 = () -> {
for (int i = 0; i < 5; i++) {
System.out.println("Task 1 - " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Runnable task2 = () -> {
for (int i = 0; i < 5; i++) {
System.out.println("Task 2 - " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
// Menjalankan tugas
executor.submit(task1);
executor.submit(task2);
// Menutup executor dan menunggu tugas selesai
executor.shutdown();
try {
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
executor.shutdownNow();
}
} catch (InterruptedException e) {
executor.shutdownNow();
}
}
}
Future dan Callable #
Jika Anda perlu mendapatkan hasil dari thread, Anda dapat menggunakan antarmuka Callable
yang mirip dengan Runnable
tetapi dapat mengembalikan nilai dan melemparkan pengecualian. Hasilnya dapat diambil dengan objek Future
.
Contoh:
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;
public class CallableExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(1);
Callable<Integer> task = () -> {
int sum = 0;
for (int i = 1; i <= 10; i++) {
sum += i;
Thread.sleep(100);
}
return sum;
};
Future<Integer> future = executor.submit(task);
try {
Integer result = future.get(); // Mendapatkan hasil dari Callable
System.out.println("Sum: " + result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
} finally {
executor.shutdown();
}
}
}
Kesimpulan #
Multithreading di Java memungkinkan aplikasi untuk melakukan banyak tugas secara bersamaan, meningkatkan efisiensi dan responsivitas. Anda dapat membuat dan mengelola thread menggunakan:
- Antarmuka
Runnable
dan kelasThread
untuk menjalankan kode dalam thread baru. - Synchronized blocks atau methods untuk menghindari konflik dalam akses sumber daya bersama.
- Executor framework untuk mengelola dan menjalankan thread dengan lebih efisien.
- Callable dan Future untuk mendapatkan hasil dari thread.
Mengelola thread dengan benar adalah penting untuk menghindari masalah seperti deadlock, race conditions, dan masalah sinkronisasi.