11
Pemrograman
Multithreaded
Program
multithreaded memuat dua atau lebih
bagian program yang dapat berjalan bersamaan. Setiap bagian program tersebut
dinamakan dengan thread, dan setiap thread mendefinisikan jalur eksekusi
sendiri. Jadi, multithreading
merupakan salah satu bentuk dari multitasking.
Kelas Thread mendefinisikan beberapa metode untuk mengelola thread,
yaitu:
Metode
|
Penjelasan
|
getName
|
Mendapatkan nama thread.
|
getPriority
|
Mendapatkan prioritas thread.
|
isAlive
|
Menentukan jika sebuah thread masih berjalan.
|
join
|
Menunggu untuk sebuah thread berhenti.
|
run
|
Titik entri untuk thread.
|
sleep
|
Menunda sebuah thread untuk periode waktu
tertentu.
|
start
|
Memulai sebuah thread dengan memanggil metode run.
|
Soal dan
Penyelesaian
1.
Tulislah sebuah program sederhana untuk menciptakan thread baru dan
menjalankannya.
Penyelesaian
// Menciptakan sebuah thread kedua.
class ThreadBaru implements Runnable
{
Thread t;
ThreadBaru() {
// Menciptakan sebuah thread
baru
t = new Thread(this, "Demo Thread");
System.out.println("Thread anak: " + t);
t.start(); // memulai thread
}
// Ini merupakan titik entri
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("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
main: " + i);
Thread.sleep(1000);
}
}
catch (InterruptedException e) {
System.out.println("Thread
main diinterupsi.");
}
System.out.println("Keluar dari thread main.");
}
}
Keluaran Program
Thread anak: Thread[Demo Thread,5,main]
Thread main: 5
Thread anak: 5
Thread anak: 4
Thread main: 4
Thread anak: 3
Thread anak: 2
Thread main: 3
Thread anak: 1
Keluar dari thread anak.
Thread main: 2
Thread main: 1
Keluar dari thread main.
Pelewatan this sebagai argumen pertama mengindikasikan bahwa Anda ingin
thread baru untuk memanggil metode run
pada objek this. Selanjutnya, start dipanggil, yang memulai thread.
Ini menyebabkan loop for pada thread
anak dimulai. Setelah memanggil start,
konstruktor ThreadBaru kembali ke main. Ketika thread main dimulai, ia
memasuki loop for. Kedua thread
sekarang berjalan bersamaan, berbagi CPU, sampai kedua loop selesai.
2.
Cara kedua dalam menciptakan sebuah thread adalah dengan menciptakan
kelas baru yang mewarisi Thread, dan
kemudian menciptakan sebuah instans dari kelas tersebut. Kelas pewaris harus
mendefinisikan-ulang metode run,
yang merupakan titik entri bagi thread baru. Kelas tersebut juga harus
memanggil start untuk memulai
eksekusi atas thread baru. Tulislah sebuah program untuk mengilustrasikan
situasi ini.
Penyelesaian
// Menciptakan thread kedua dengan mewarisi dari kelas Thread
class ThreadBaru extends Thread {
ThreadBaru() {
// Menciptakan thread baru
super("Demo Thread");
System.out.println("Thread anak: " + this);
start(); // memulai thread
}
// Ini adalah titik entri 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
PewarisanThread {
public static void main(String args[]) {
new ThreadBaru(); // menciptakan sebuah thread baru
try {
for(int i = 5; i > 0;
i--) {
System.out.println("Thread
main: " + i);
Thread.sleep(1000);
}
}
catch (InterruptedException e) {
System.out.println("Thread
main diinterupsi.");
}
System.out.println("Keluar dari thread main.");
}
}
Keluaran Program
Thread anak: Thread[Demo Thread,5,main]
Thread main: 5
Thread anak: 5
Thread anak: 4
Thread main: 4
Thread anak: 3
Thread anak: 2
Thread main: 3
Thread anak: 1
Keluar dari thread anak.
Thread main: 2
Thread main: 1
Keluar dari thread main.
3.
Sejauh ini, Anda menggunakan dua thread: thread main dan satu thread anak.
Namun, program Anda dapat memiliki sebanyak mungkin thread yang Anda inginkan.
Tulislah sebuah program untuk mengilustrasikan situasi ini.
Penyelesaian
// Menciptakan thread jamak.
class ThreadBaru implements Runnable
{
String nama; // name of thread
Thread t;
ThreadBaru(String namaThread) {
nama = namaThread;
t = new Thread(this, nama);
System.out.println("Thread baru: " + t);
t.start(); // memulai thread
}
// Ini merupakan titik entri
untuk thread.
public void run() {
try {
for(int i = 5; i > 0;
i--) {
System.out.println(nama + ": " + i);
Thread.sleep(1000);
}
}
catch (InterruptedException
e) {
System.out.println(nama
+ " diinterupsi");
}
System.out.println(nama +
" keluar.");
}
}
public class
DemoThreadJamak {
public static void main(String args[]) {
new ThreadBaru("Satu");
// memulai tiga thread
new ThreadBaru("Dua");
new ThreadBaru("Tiga");
try {
// menunggu untuk thread
lain berakhir
Thread.sleep(10000);
}
catch (InterruptedException e) {
System.out.println("Thread
main diinterupsi");
}
System.out.println("Keluar dari thread main.");
}
}
Keluaran Program
Thread baru: Thread[Satu,5,main]
Thread baru: Thread[Dua,5,main]
Thread baru: Thread[Tiga,5,main]
Satu: 5
Dua: 5
Tiga: 5
Tiga: 4
Dua: 4
Satu: 4
Tiga: 3
Dua: 3
Satu: 3
Tiga: 2
Satu: 2
Dua: 2
Satu: 1
Dua: 1
Tiga: 1
Satu keluar.
Tiga keluar.
Dua keluar.
Keluar dari thread main.
4.
Tulis-ulanglah kode pada soal no 3, dengan menggunakan metode join untuk memastikan bahwa thread main
merupakan thread terakhir yang berhenti. Program yang ditulis juga
mendemonstrasikan metode isAlive.
Penyelesaian
// Menggunakan metode join() untuk menunggu thread selesai.
class ThreadBaru implements Runnable
{
String nama; // nama thread
Thread t;
ThreadBaru(String namaThread) {
nama = namaThread;
t = new Thread(this, nama);
System.out.println("Thread baru: " + t);
t.start(); // memulai thread
}
// Ini merupakan titik entri
untuk thread.
public void run() {
try {
for(int i = 5; i > 0;
i--) {
System.out.println(nama + ": " + i);
Thread.sleep(1000);
}
}
catch (InterruptedException e) {
System.out.println(nama
+ " diinterupsi.");
}
System.out.println(nama +
" keluar.");
}
}
public class
DemoJoin {
public static void main(String args[]) {
ThreadBaru ob1 = new ThreadBaru("Satu");
ThreadBaru ob2 = new ThreadBaru("Dua");
ThreadBaru ob3 = new ThreadBaru("Tiga");
System.out.println("Thread Satu berjalan: "
+ ob1.t.isAlive());
System.out.println("Thread Dua berjalan: "
+ ob2.t.isAlive());
System.out.println("Thread Tiga berjalan: "
+ ob3.t.isAlive());
// menunggu thread-thread
tersebut selesai
try {
System.out.println("Menunggu untuk thread-thread selesai.");
ob1.t.join();
ob2.t.join();
ob3.t.join();
}
catch (InterruptedException
e) {
System.out.println("Thread
main diinterupsi");
}
System.out.println("Thread Satu berjalan: "
+ ob1.t.isAlive());
System.out.println("Thread Dua berjalan: "
+ ob2.t.isAlive());
System.out.println("Thread Tiga berjalan: "
+ ob3.t.isAlive());
System.out.println("Thread main keluar.");
}
}
Keluaran Program
Thread baru: Thread[Satu,5,main]
Thread baru: Thread[Dua,5,main]
Thread baru: Thread[Tiga,5,main]
Satu: 5
Thread Satu berjalan: true
Thread Dua berjalan: true
Thread Tiga berjalan: true
Dua: 5
Menunggu untuk thread-thread selesai.
Tiga: 5
Satu: 4
Dua: 4
Tiga: 4
Tiga: 3
Satu: 3
Dua: 3
Tiga: 2
Satu: 2
Dua: 2
Dua: 1
Satu: 1
Tiga: 1
Dua keluar.
Satu keluar.
Tiga keluar.
Thread Satu berjalan: false
Thread Dua berjalan: false
Thread Tiga berjalan: false
Thread main keluar.
5.
Prioritas thread dipakai untuk memutuskan kapan setiap thread diijinkan
untuk berjalan. Anda dapat menetapkan prioritas sebuah thread menggunakan
metode setPriority. Tulislah sebuah
program untuk mendemonstrasikan dua thread dengan dua prioritas yang berbeda.
Satu thread ditetapkan dua level di atas prioritas normal, yang didefinisikan
oleh Thread.NORM_PRIORITY, dan
thread lain ditetapkan dua level di bawahnya. Kedua thread dijalankan selamat
sepuluh detik. Setiap thread mengeksekusi sebuah loop, menghitung jumlah
iterasi. Setelah sepuluh detik, thread main menghentikan kedua thread tersebut.
Jumlah iterasi dari kedua thread kemudian ditampilkan.
Penyelesaian
// Mendemonstrasikan prioritas thread.
class Pewaktu implements Runnable {
long waktu = 0;
Thread t;
private volatile boolean berjalan = true;
public Pewaktu(int p) {
t = new Thread(this);
t.setPriority(p);
}
public void run() {
while (berjalan) {
waktu++;
}
}
public void stop() {
berjalan = false;
}
public void start() {
t.start();
}
}
public class
DemoPrioritasThread {
public static void main(String args[]) {
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
Pewaktu tinggi = new Pewaktu(Thread.NORM_PRIORITY + 2);
Pewaktu rendah = new Pewaktu(Thread.NORM_PRIORITY - 2);
rendah.start();
tinggi.start();
try {
Thread.sleep(10000);
}
catch (InterruptedException e) {
System.out.println("Thread
main diinterupsi.");
}
rendah.stop();
tinggi.stop();
// Menunggu thread-thread
anak berhenti.
try {
tinggi.t.join();
rendah.t.join();
}
catch (InterruptedException
e) {
System.out.println("InterruptedException
ditangkap");
}
System.out.println("Thread prioritas-rendah: " +
rendah.waktu);
System.out.println("Thread prioritas-tingi: " +
tinggi.waktu);
}
}
Keluaran Program
Thread prioritas-rendah: 3655610649
Thread prioritas-tingi: 3673799854
6.
Ketika dua atau lebih thread memerlukan akses terhadap sumberdaya yang
sama, maka diperlukan sebuah cara untuk memastikan bahwa sumberdaya akan
digunakan oleh satu thread pada suatu waktu. Proses ini dikenal dengan
sinkronisasi.
Untuk memahami kebutuhan akan sinkronisasi,
tulislah sebuah program untuk mengilustrasikannya. Program tersebut memiliki
tiga kelas sederhana. Kelas pertama, Panggilsaya,
yang memiliki satu metode bernama panggil.
Metode panggil memerlukan sebuah
parameter String yang bernama psn. Metode ini mencoba untuk
menampilkan string psn di dalam
kurung siku. Hal menarik untuk diperhatikan adalah bahwa setelah panggil menampilkan kurung siku pembuka
dan string psn, ia memanggil Thread.sleep(1000), yang menunda thread
tersebut selama satu detik.
Konstruktor dari kelas berikutnya, Pemanggil, memerlukan sebuah referensi
yang menunjuk ke suatu instans dari kelas Panggilsaya
dan sebuah String, yang disimpan di
dalam target dan psn. Konstruktor tersebut juga
menciptakan sebuah thread baru yang akan memanggil metode run dari objek tersebut. Thread itu dimulai. Metode run untuk Pemanggil memanggil metode panggil
pada instans target dari Panggilsaya, melewatkan string psn. Terakhir, kelas Sinkron dimulai dengan menciptakan
sebuah instans dari Panggilsaya, dan
tiga instans dari Pemanggil,
masing-masing dengan string pesan unik. Instans sama dari Panggilsaya dilewatkan kepada tiap Pemanggil.
Penyelesaian
// Program ini tidak disinkronisasi.
class Panggilsaya {
void panggil(String msg)
{
System.out.print("[" + msg);
try {
Thread.sleep(1000);
}
catch(InterruptedException e) {
System.out.println("Diinterupsi");
}
System.out.println("]");
}
}
class Pemanggil implements Runnable
{
String psn;
Panggilsaya target;
Thread t;
public Pemanggil(Panggilsaya targ, String s) {
target = targ;
psn = s;
t = new Thread(this);
t.start();
}
public void run() {
target.panggil(psn);
}
}
public class
DemoSinkronisasiThread {
public static void main(String args[]) {
Panggilsaya target = new Panggilsaya();
Pemanggil ob1 = new Pemanggil(target, "Dunia");
Pemanggil ob2 = new Pemanggil(target, "Sinkronisasi");
Pemanggil ob3 = new Pemanggil(target, "Thread");
// Tunggu thread-thread
berhenti
try {
ob1.t.join();
ob2.t.join();
ob3.t.join();
}
catch(InterruptedException e) {
System.out.println("Diinterupsi");
}
}
}
Keluaran Program
[Dunia[Sinkronisasi[Thread]
]
]
7.
Tulis-ulanglah kode pada soal no 6 agar menggunakan katakunci synchronized untuk mensinkronisasi
thread.
Penyelesaian
// Program ini tidak disinkronisasi.
class Panggilsaya {
void panggil(String msg)
{
System.out.print("[" + msg);
try {
Thread.sleep(1000);
}
catch(InterruptedException e) {
System.out.println("Diinterupsi");
}
System.out.println("]");
}
}
class Pemanggil implements Runnable
{
String psn;
Panggilsaya target;
Thread t;
public Pemanggil(Panggilsaya targ, String s) {
target = targ;
psn = s;
t = new Thread(this);
t.start();
}
// sinkronisasi
pemanggilan-pemanggilan terhadap panggil()
public void run() {
synchronized(target) { //
blok tersinkronisasi
target.panggil(psn);
}
}
}
public class
DemoSinkronisasiThread2 {
public static void main(String args[]) {
Panggilsaya target = new Panggilsaya();
Pemanggil ob1 = new Pemanggil(target, "Dunia");
Pemanggil ob2 = new Pemanggil(target, "Sinkronisasi");
Pemanggil ob3 = new Pemanggil(target, "Thread");
// Tunggu thread-thread
berhenti
try {
ob1.t.join();
ob2.t.join();
ob3.t.join();
}
catch(InterruptedException
e) {
System.out.println("Diinterupsi");
}
}
}
Keluaran Program
[Dunia]
[Thread]
[Sinkronisasi]
8.
Jave memuat mekanisme komunikasi antar-thread menggunakan metode wait, notify, dan notifyAll.
Ketiga metode ini diimplementasikan sebagai metode final di dalam kelas Object, jadi semua kelas memilikinya.
Ketiga metode hanya dapat dipanggil dari dalam konteks tersinkronisasi. Berikut
penjelasan dari ketiga metode tersebut:
·
wait memberitahu
thread pemanggil untuk beristrahat sampai thread lain berjalan dan memanggil notify.
·
notify membangunkan
sebuah thread yang memanggil wait
pada objek yang sama.
·
notifyAll membangunkan
semua thread yang memanggil wait
pada objek yang sama. Salah satu dari thread tersebut akan diberikan akses.
Tulislah sebuah program yang secara
tidak tepat mengimplementasikan suatu permasalahan produser/konsumen. Program
memuat empat kelas: Antrian, antrian
yang akan disinkronisasi; Produser,
objek yang menghasilkan entri-antri antrian; Konsumen, objek yang mengkonsumsi entri-entri antrian; dan DemoKomunikasiThread, kelas yang
menciptakan satu Antrian, satu Produser, dan satu Konsumen.
Penyelesaian
// Implementasi komunikasi antar thread yang tidak tepat.
class Antrian {
int n;
synchronized int ambil() {
System.out.println("Mengambil: " + n);
return n;
}
synchronized void
tempatkan(int n) {
this.n = n;
System.out.println("Menempatkan: " + n);
}
}
class Produser implements Runnable {
Antrian q;
Produser(Antrian q) {
this.q = q;
new Thread(this, "Produser").start();
}
public void run() {
int i = 0;
while(true) {
q.tempatkan(i++);
}
}
}
class Konsumen implements Runnable {
Antrian q;
Konsumen(Antrian q) {
this.q = q;
new Thread(this, "Konsumen").start();
}
public void run() {
while(true) {
q.ambil();
}
}
}
public class
DemoKomunikasiThread {
public static void main(String args[]) {
Antrian q = new Antrian();
new Produser(q);
new Konsumen(q);
System.out.println("Tekan Control-C untuk menghentikan.");
}
}
Keluaran Program
Menempatkan: 0
Menempatkan: 1
Menempatkan: 2
Menempatkan: 3
Menempatkan: 4
Menempatkan: 5
Tekan Control-C untuk menghentikan.
Menempatkan: 6
Menempatkan: 7
Menempatkan: 8
Menempatkan: 9
Menempatkan: 10
Menempatkan: 11
Menempatkan: 12
Mengambil: 12
Mengambil: 12
Mengambil: 12
Mengambil: 12
Mengambil: 12
Mengambil: 12
Mengambil: 12
Mengambil: 12
9.
Tulis-ulanglah kode pada soal no 8 dengan menggunakan wait dan notify.
Penyelesaian
// Implementasi komunikasi antar thread yang tepat.
class Antrian {
int n;
boolean nilaiSet = false;
synchronized int ambil()
{
while(!nilaiSet)
try {
wait();
}
catch(InterruptedException e) {
System.out.println("InterruptedException
ditangkap");
}
System.out.println("Mengambil: " + n);
nilaiSet = false;
notify();
return n;
}
synchronized void
tempatkan(int n) {
while(nilaiSet)
try {
wait();
}
catch(InterruptedException e) {
System.out.println("InterruptedException
ditangkap");
}
this.n = n;
nilaiSet = true;
System.out.println("Menempatkan:
" + n);
notify();
}
}
class Produser implements Runnable {
Antrian q;
Produser(Antrian q) {
this.q = q;
new Thread(this, "Produser").start();
}
public void run() {
int i = 0;
while(true) {
q.tempatkan(i++);
}
}
}
class Konsumen implements Runnable {
Antrian q;
Konsumen(Antrian q) {
this.q = q;
new Thread(this, "Konsumen").start();
}
public void run() {
while(true) {
q.ambil();
}
}
}
public class
DemoKomunikasiThread2 {
public static void main(String args[]) {
Antrian q = new Antrian();
new Produser(q);
new Konsumen(q);
System.out.println("Tekan Control-C untuk menghentikan.");
}
}
Keluaran Program
Menempatkan: 0
Tekan Control-C untuk menghentikan.
Mengambil: 0
Menempatkan: 1
Mengambil: 1
Menempatkan: 2
Mengambil: 2
Menempatkan: 3
Mengambil: 3
Menempatkan: 4
Mengambil: 4
Menempatkan: 5
Mengambil: 5
Menempatkan: 6
Mengambil: 6
Menempatkan: 7
Mengambil: 7
Menempatkan: 8
Mengambil: 8
Menempatkan: 9
Mengambil: 9
Menempatkan: 10
Mengambil: 10
10. Tulislah sebuah program
untuk menggunakan metode suspend dan
resume.
Penyelesaian
// Menggunakan suspend() dan resume().
class ThreadBaru implements Runnable
{
String nama; // nama thread
Thread t;
ThreadBaru(String namaThread) {
nama = namaThread;
t = new Thread(this, nama);
System.out.println("Thread baru: " + t);
t.start(); // memulai thread
}
// ini adalah titik entri untuk
thread.
public void run() {
try {
for(int i = 15; i >
0; i--) {
System.out.println(nama + ": " + i);
Thread.sleep(200);
}
}
catch (InterruptedException e) {
System.out.println(nama
+ " diinterupsi.");
}
System.out.println(nama +
" keluar.");
}
}
public class
DemoSuspendResume {
public static void main(String args[]) {
ThreadBaru ob1 = new ThreadBaru("Satu");
ThreadBaru ob2 = new ThreadBaru("Dua");
try {
Thread.sleep(1000);
ob1.t.suspend();
System.out.println("Membatalkan
thread Satu");
Thread.sleep(1000);
ob1.t.resume();
System.out.println("Memulai
thread Satu");
ob2.t.suspend();
System.out.println("Membatalkan
thread Dua");
Thread.sleep(1000);
ob2.t.resume();
System.out.println("Memulai
thread Dua");
}
catch (InterruptedException e) {
System.out.println("Thread
main diinterupsi");
}
// menunggu thread-thread
selesai
try {
System.out.println("Menunggu thread-thread selesai.");
ob1.t.join();
ob2.t.join();
}
catch (InterruptedException e) {
System.out.println("Thread
main diinterupsi");
}
System.out.println("Thread main keluar.");
}
}
Keluaran Program
Thread baru: Thread[Satu,5,main]
Thread baru: Thread[Dua,5,main]
Satu: 15
Dua: 15
Satu: 14
Dua: 14
Dua: 13
Satu: 13
Dua: 12
Satu: 12
Dua: 11
Satu: 11
Dua: 10
Membatalkan thread Satu
Dua: 9
Dua: 8
Dua: 7
Dua: 6
Dua: 5
Memulai thread Satu
Membatalkan thread Dua
Satu: 10
Satu: 9
Satu: 8
Satu: 7
Satu: 6
Satu: 5
Memulai thread Dua
Menunggu thread-thread selesai.
Dua: 4
Satu: 4
Dua: 3
Satu: 3
Dua: 2
Satu: 2
Dua: 1
Satu: 1
Dua keluar.
Satu keluar.
Thread main keluar.
11. Tulis-ulanglah
kode pada soal no 10 sehingga membatalkan dan memulai lagi sebuah thread dengan
cara yang lebih elegan.
Penyelesaian
// Menggunakan suspend() dan resume() dengan cara yang lebih modern
class ThreadBaru implements Runnable
{
String nama; // nama thread
Thread t;
boolean benderaBatal;
ThreadBaru(String namaThread) {
nama = namaThread;
t = new Thread(this, nama);
System.out.println("Thread baru: " + t);
benderaBatal = false;
t.start(); // memulai thread
}
// ini adalah titik entri untuk
thread.
public void run() {
try {
for(int i = 15; i >
0; i--) {
System.out.println(nama + ": " + i);
Thread.sleep(200);
synchronized(this) {
while(benderaBatal) {
wait();
}
}
}
}
catch (InterruptedException e) {
System.out.println(nama
+ " diinterupsi.");
}
System.out.println(nama +
" keluar.");
}
void batalku() {
benderaBatal = true;
}
synchronized void
mulaiku() {
benderaBatal = false;
notify();
}
}
public class
DemoSuspendResume2 {
public static void main(String args[]) {
ThreadBaru ob1 = new ThreadBaru("Satu");
ThreadBaru ob2 = new ThreadBaru("Dua");
try {
Thread.sleep(1000);
ob1.batalku();
System.out.println("Membatalkan thread Satu");
Thread.sleep(1000);
ob1.mulaiku();
System.out.println("Memulai
thread Satu");
ob2.batalku();
System.out.println("Membatalkan
thread Dua");
Thread.sleep(1000);
ob2.mulaiku();
System.out.println("Memulai
thread Dua");
}
catch (InterruptedException e) {
System.out.println("Thread
main diinterupsi");
}
// menunggu thread-thread
selesai
try {
System.out.println("Menunggu
thread-thread selesai.");
ob1.t.join();
ob2.t.join();
}
catch (InterruptedException e) {
System.out.println("Thread
main diinterupsi");
}
System.out.println("Thread main keluar.");
}
}
Keluaran Program
Thread baru: Thread[Satu,5,main]
Thread baru: Thread[Dua,5,main]
Satu: 15
Dua: 15
Dua: 14
Satu: 14
Dua: 13
Satu: 13
Satu: 12
Dua: 12
Dua: 11
Satu: 11
Dua: 10
Membatalkan thread Satu
Dua: 9
Dua: 8
Dua: 7
Dua: 6
Dua: 5
Memulai thread Satu
Membatalkan thread Dua
Satu: 10
Satu: 9
Satu: 8
Satu: 7
Satu: 6
Satu: 5
Dua: 4
Memulai thread Dua
Menunggu thread-thread selesai.
Satu: 4
Dua: 3
Satu: 3
Dua: 2
Satu: 2
Dua: 1
Dua keluar.
Satu: 1
Satu keluar.
Thread main keluar.
No comments:
Post a Comment