Thread adalah unit dasar operasi sistem operasi. Ini dienkapsulasi dalam suatu proses. Meskipun kita tidak membuat thread secara manual, prosesnya akan menjalankan thread default.
Untuk JVM, ketika kita menulis program single-thread untuk dijalankan, setidaknya ada dua thread yang berjalan di JVM, satu adalah program yang kita buat, dan yang lainnya adalah pengumpulan sampah.
Informasi dasar benang
Kita dapat memperoleh beberapa informasi tentang thread saat ini melalui metode Thread.currentThread() dan memodifikasinya.
Mari kita lihat kode berikut:
Thread.currentThread().setName("Uji");
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
nama = Thread.currentThread().getName();
prioritas = Thread.currentThread().getPriority();
groupName = Utas.currentThread().getThreadGroup().getName();
isDaemon = Utas.currentThread().isDaemon();
System.out.println("Nama Thread:"+nama);
System.out.println("Prioritas:" + prioritas);
GroupName , setiap thread akan berada dalam grup thread secara default. Kita juga dapat secara eksplisit membuat grup thread. Grup thread juga dapat berisi grup sub-thread, sehingga thread dan grup thread membentuk struktur pohon.
Nama , setiap thread akan memiliki nama. Jika tidak ditentukan secara eksplisit, aturan namanya adalah "Thread-xxx".
Priority , setiap thread akan memiliki prioritasnya sendiri, dan cara JVM menangani prioritas adalah "preemptive". Ketika JVM menemukan thread dengan prioritas tinggi, JVM segera menjalankan thread tersebut; untuk beberapa thread dengan prioritas yang sama, JVM akan melakukan polling terhadap thread tersebut. Prioritas thread Java berkisar dari 1 hingga 10, dengan defaultnya adalah 5. Kelas Thread mendefinisikan dua konstanta: MIN_PRIORITY dan MAX_PRIORITY untuk mewakili prioritas tertinggi dan terendah.
Kita dapat melihat kode berikut, yang mendefinisikan dua thread dengan prioritas berbeda:
Thread thread2 = Thread baru("tinggi")
{
menjalankan kekosongan publik()
{
untuk (int saya = 0; saya < 5; saya++)
{
System.out.println("Thread 2 sedang berjalan.");
}
}
};
thread1.setPriority(Utas.MIN_PRIORITY);
thread2.setPriority(Utas.MAX_PRIORITY);
thread1.mulai();
thread2.mulai();
}
thread1.mulai();
}
Cara membuat utas
Konten di atas semuanya menunjukkan beberapa informasi di thread default, jadi bagaimana cara membuat thread? Di Java, kami memiliki 3 cara untuk membuat thread.
Thread di Java mewarisi kelas Thread atau mengimplementasikan antarmuka Runnable. Mari kita bahas satu per satu.
Gunakan kelas dalam untuk membuat thread
Kita bisa menggunakan kelas dalam untuk membuat thread. Prosesnya adalah mendeklarasikan variabel bertipe Thread dan mengganti metode run. Contoh kodenya adalah sebagai berikut:
Kita dapat memperoleh kelas dari Thread dan mengganti metode eksekusinya dengan cara yang mirip dengan cara di atas. Contoh kodenya adalah sebagai berikut:
kekosongan statis publik createThreadBySubClass()
{
Utas MyThread = baru MyThread();
thread.mulai();
}
Kita dapat mendefinisikan kelas untuk mengimplementasikan antarmuka Runnable, dan kemudian menggunakan instance kelas ini sebagai parameter untuk membangun konstruktor variabel Thread. Contoh kodenya adalah sebagai berikut:
kekosongan statis publik createThreadByRunnable()
{
MyRunnable runnable = baru MyRunnable();
Thread thread = Thread baru (dapat dijalankan);
thread.mulai();
}
Ini melibatkan mode multi-threading yang berjalan di Java. Untuk Java, saat multi-threading berjalan, ada perbedaan antara "multi-objek multi-threading" dan "multi-threading objek tunggal":
Multi-objek multi-threading , program membuat beberapa objek thread selama berjalan, dan satu thread berjalan pada setiap objek.
Multi-threading objek tunggal , program membuat objek thread saat dijalankan dan menjalankan banyak thread di dalamnya.
Jelas, dari perspektif sinkronisasi dan penjadwalan thread, multi-objek multi-threading lebih sederhana. Dari tiga metode pembuatan thread di atas, dua metode pertama adalah "multi-objek multi-thread", dan metode ketiga dapat menggunakan "multi-objek multi-thread" atau "single-object single-thread".
Mari kita lihat contoh kode berikut, yang akan menggunakan metode Object.notify. Metode ini akan membangunkan thread pada objek; dan metode Object.notifyAll akan membangunkan semua thread pada objek.
public static void main(String[] args) melempar InterruptedException
{
beri tahuUji();
beri tahuTest2();
beri tahuTest3();
}
private static void notifyTest() menampilkan InterruptedException
{
Benang Saya[] arrThreads = Benang Saya baru[3];
untuk (int i = 0; i < arrThreads.length; i++)
{
arrThreads[i] = new MyThread();
arrBenang[i].id = i;
arrThreads[i].setDaemon(benar);
arrThreads[i].mulai();
}
Thread.tidur(500);
untuk (int i = 0; i < arrThreads.length; i++)
{
disinkronkan(arrThreads[i])
{
arrThreads[i].notify();
}
}
}
private static void notifyTest2() menampilkan InterruptedException
{
MyRunner[] arrMyRunners = MyRunner baru[3];
Utas[] arrThreads = Utas baru[3];
untuk (int i = 0; i < arrThreads.length; i++)
{
arrMyRunners[i] = new MyRunner();
arrMyRunners[i].id = saya;
arrThreads[i] = Thread baru(arrMyRunners[i]);
arrThreads[i].setDaemon(benar);
arrThreads[i].mulai();
}
Thread.tidur(500);
untuk (int i = 0; i < arrMyRunners.length; i++)
{
disinkronkan(arrMyRunners[i])
{
arrMyRunners[i].notify();
}
}
}
private static void notifyTest3() menampilkan InterruptedException
{
Pelari MyRunner = MyRunner baru();
Utas[] arrThreads = Utas baru[3];
untuk (int i = 0; i < arrThreads.length; i++)
{
arrThreads[i] = Thread baru(pelari);
arrThreads[i].setDaemon(benar);
arrThreads[i].mulai();
}
Thread.tidur(500);
disinkronkan (pelari)
{
pelari.notifyAll();
}
}
}
kelas MyThread memperluas Thread
{
id int publik = 0;
menjalankan kekosongan publik()
{
System.out.println("Thread " + id + " siap untuk tidur selama 5 menit.");
mencoba
{
disinkronkan (ini)
{
ini.tunggu(5*60*1000);
}
}
menangkap (InterruptedException ex)
{
ex.printStackTrace();
}
System.out.println("Th" + id + "thread telah dibangunkan.");
}
}
kelas MyRunner mengimplementasikan Runnable
{
id int publik = 0;
menjalankan kekosongan publik()
{
System.out.println("Thread " + id + " siap untuk tidur selama 5 menit.");
mencoba
{
disinkronkan (ini)
{
ini.tunggu(5*60*1000);
}
}
menangkap (InterruptedException ex)
{
ex.printStackTrace();
}
System.out.println("Th" + id + "thread telah dibangunkan.");
}
}
Metode notifyAll cocok untuk skenario "objek tunggal multi-thread", karena metode notify hanya akan membangunkan satu thread pada objek secara acak.
Peralihan status utas
Untuk thread, dari saat kita membuatnya hingga thread berakhir, status thread selama proses ini mungkin seperti ini:
Penciptaan: Sudah ada instance Thread, namun CPU masih memiliki sumber daya dan potongan waktu yang dialokasikan padanya.
Siap: Thread telah memperoleh semua sumber daya yang dibutuhkan untuk berjalan dan tinggal menunggu CPU menjadwalkan waktunya.
Berjalan: Thread terletak di irisan waktu CPU saat ini dan menjalankan logika terkait.
Sleep: Umumnya keadaan setelah pemanggilan Thread.sleep Pada saat ini, thread masih menyimpan berbagai sumber daya yang diperlukan untuk pengoperasian, namun tidak akan dijadwalkan oleh CPU.
Suspend: Umumnya keadaan setelah pemanggilan Thread.suspend. Mirip dengan sleep, CPU tidak akan menjadwalkan thread. Perbedaannya adalah pada keadaan ini, thread akan melepaskan semua sumber daya.
Kematian: Thread berakhir atau metode Thread.stop dipanggil.
Selanjutnya kita akan mendemonstrasikan cara mengganti status thread. Pertama kita akan menggunakan metode berikut:
Thread() atau Thread(Runnable): Buatlah sebuah thread.
Thread.start: Memulai thread.
Thread.sleep: Mengalihkan thread ke kondisi tidur.
Thread.interrupt: Mengganggu eksekusi thread.
Thread.join: Tunggu hingga thread berakhir.
Thread.yield: Hilangkan thread dari potongan waktu eksekusinya pada CPU dan tunggu penjadwalan berikutnya.
Object.wait: Mengunci semua thread pada Object dan tidak akan terus berjalan hingga metode notify muncul.
Object.notify: Secara acak membangunkan thread pada Object.
Object.notifyAll: Aktifkan semua thread di Object.
Sekarang, waktunya demonstrasi! ! !
Thread menunggu dan bangun
Metode Object.wait dan Object.notify terutama digunakan di sini, silakan lihat contoh notifikasi di atas. Perlu dicatat bahwa wait dan notify harus menargetkan objek yang sama. Saat kita membuat thread dengan mengimplementasikan antarmuka Runnable, kita harus menggunakan kedua metode ini pada objek Runnable, bukan pada objek Thread.
Benang tidur dan bangun
public static void main(String[] args) melempar InterruptedException
{
tes tidur();
}
private static void sleepTest() menampilkan InterruptedException
{
Utas utas = Utas baru()
{
menjalankan kekosongan publik()
{
System.out.println("Thread" + Thread.currentThread().getName() + "Ini akan tidur selama 5 menit.");
mencoba
{
Thread.tidur(5*60*1000);
}
menangkap (InterruptedException ex)
{
System.out.println("Thread" + Thread.currentThread().getName() + "Tidur terganggu.");
}
System.out.println("Thread" + Thread.currentThread().getName() + "Tidur berakhir.");
}
};
thread.setDaemon(benar);
thread.mulai();
Thread.tidur(500);
thread.interrupt();
}
}
Meskipun ada metode Thread.stop, metode ini tidak disarankan. Kita dapat menggunakan mekanisme tidur dan bangun di atas untuk membiarkan thread mengakhiri thread saat memproses IterruptedException.
public static void main(String[] args) melempar InterruptedException
{
berhentiTes();
}
private static void stopTest() melempar InterruptedException
{
Utas utas = Utas baru()
{
menjalankan kekosongan publik()
{
System.out.println("Thread sedang berjalan.");
mencoba
{
Thread.tidur(1*60*1000);
}
menangkap (InterruptedException ex)
{
System.out.println("Interupsi thread, thread akhir");
kembali;
}
System.out.println("Utas berakhir normal.");
}
};
thread.mulai();
Thread.tidur(500);
thread.interrupt();
}
}
Ketika kita membuat 10 sub-thread di thread utama, dan kemudian kita berharap setelah semua 10 sub-thread selesai, thread utama akan menjalankan logika berikutnya.
public static void main(String[] args) melempar InterruptedException
{
ikut Tes();
}
private static void joinTest() menampilkan InterruptedException
{
Utas utas = Utas baru()
{
menjalankan kekosongan publik()
{
mencoba
{
untuk(int saya = 0; saya < 5; saya++)
{
System.out.println("Thread sedang berjalan.");
Thread.tidur(1000);
}
}
menangkap (InterruptedException ex)
{
ex.printStackTrace();
}
}
};
thread.setDaemon(benar);
thread.mulai();
Thread.tidur(1000);
thread.join();
System.out.println("Thread utama berakhir dengan normal.");
}
}
Kita tahu bahwa semua thread dalam suatu proses berbagi ruang memori, jadi bagaimana kita mentransfer pesan antar thread yang berbeda? Saat meninjau Java I/O, kita membahas tentang PipedStream dan PipedReader, dan di sinilah keduanya berperan.
Dua contoh berikut memiliki fungsi yang sama persis. Perbedaannya adalah yang satu menggunakan Stream dan yang lainnya menggunakan Reader/Writer.
Utas utas1 = Utas baru()
{
menjalankan kekosongan publik()
{
BufferedReader br = BufferedReader baru(InputStreamReader(Sistem.in) baru);
mencoba
{
sementara (benar)
{
Pesan string = br.readLine();
pos.write(pesan.getBytes());
if (message.equals("end")) rusak;
}
br.close();
pos.close();
}
menangkap (Pengecualian ex)
{
ex.printStackTrace();
}
}
};
Utas utas2 = Utas baru()
{
menjalankan kekosongan publik()
{
byte[] penyangga = byte baru[1024];
int byteBaca = 0;
mencoba
{
while((bytesRead = pis.read(buffer, 0, buffer.length)) != -1)
{
System.out.println(String baru(buffer));
if (String baru(buffer).equals("end")) break;
penyangga = nol;
buffer = byte baru[1024];
}
pis.close();
penyangga = nol;
}
menangkap (Pengecualian ex)
{
ex.printStackTrace();
}
}
};
thread1.setDaemon(benar);
thread2.setDaemon(benar);
thread1.mulai();
thread2.mulai();
thread1.join();
thread2.join();
}
Utas utas1 = Utas baru()
{
menjalankan kekosongan publik()
{
BufferedReader br = BufferedReader baru(InputStreamReader(Sistem.in) baru);
mencoba
{
sementara (benar)
{
Pesan string = br.readLine();
bw.write(pesan);
bw.newLine();
bw.flush();
if (message.equals("end")) rusak;
}
br.close();
pw.tutup();
bw.close();
}
menangkap (Pengecualian ex)
{
ex.printStackTrace();
}
}
};
Utas utas2 = Utas baru()
{
menjalankan kekosongan publik()
{
Garis string = null;
mencoba
{
while((baris = br.readLine()) != null)
{
System.out.println(baris);
if (line.equals("end")) break;
}
br.close();
pr.tutup();
}
menangkap (Pengecualian ex)
{
ex.printStackTrace();
}
}
};
thread1.setDaemon(benar);
thread2.setDaemon(benar);
thread1.mulai();
thread2.mulai();
thread1.join();
thread2.join();
}