1. Proses dan utas
1. Apa prosesnya?
Definisi sempit: Suatu proses adalah contoh dari program komputer yang sedang dieksekusi.
Definisi Umum: Suatu proses adalah aktivitas yang berjalan dari suatu program dengan fungsi independen tertentu mengenai kumpulan data tertentu. Ini adalah unit dasar pelaksanaan dinamis dari sistem operasi. Dalam sistem operasi tradisional, proses adalah unit alokasi dasar dan unit eksekusi dasar.
2. Apa itu utas?
Thread, kadang -kadang disebut proses ringan (LWP), adalah unit terkecil dari aliran eksekusi program. Utas standar terdiri dari ID utas, pointer instruksi saat ini (PC), satu set register, dan tumpukan. Selain itu, utas adalah entitas dalam proses dan merupakan unit dasar yang dijadwalkan secara independen dan dikirim oleh sistem. Utas itu sendiri tidak memiliki sumber daya sistem, tetapi hanya memiliki beberapa sumber daya penting selama operasi, tetapi dapat berbagi semua sumber daya yang dimiliki oleh proses dengan utas lain yang termasuk dalam proses yang sama.
3. Apa perbedaan antara suatu proses dan utas?
Perbedaan utama antara proses dan utas adalah bahwa mereka adalah metode manajemen sumber daya sistem operasi yang berbeda.
Suatu proses memiliki ruang alamat yang independen. Setelah proses macet, itu tidak akan mempengaruhi proses lain dalam mode yang dilindungi, dan utas hanyalah jalur eksekusi yang berbeda dalam suatu proses.
Utas memiliki tumpukan dan variabel lokal sendiri, tetapi tidak ada ruang alamat terpisah antara utas. Jika utas mati, itu berarti bahwa seluruh proses mati. Oleh karena itu, program multi-proses lebih kuat daripada program multi-thread, tetapi ketika beralih proses, mereka mengkonsumsi lebih banyak sumber daya dan kurang efisien. Namun, untuk beberapa operasi bersamaan yang membutuhkan operasi bersamaan yang memerlukan berbagi variabel tertentu, mereka hanya dapat menggunakan utas, bukan proses.
Singkatnya, perbedaan antara utas dan proses adalah:
(1) Suatu program memiliki setidaknya satu proses, dan suatu proses memiliki setidaknya satu utas;
(2) Skala divisi utas lebih kecil dari proses, membuat konkurensi program multi-threaded tinggi.
(3) Proses ini memiliki unit memori independen selama eksekusi, dan beberapa utas berbagi memori, yang sangat meningkatkan efisiensi operasi program.
(4) Ada perbedaan antara utas dan proses selama eksekusi. Setiap utas independen memiliki entri untuk eksekusi program, urutan eksekusi dan keluar untuk program. Namun, utas tidak dapat dieksekusi secara independen, dan harus ada dalam aplikasi, dan beberapa kontrol eksekusi utas disediakan oleh aplikasi.
(5) Dari sudut pandang logis, arti multi-threading terletak pada aplikasi, beberapa bagian eksekusi dapat dieksekusi pada saat yang sama. Namun, sistem operasi tidak menganggap beberapa utas sebagai beberapa aplikasi independen untuk mewujudkan penjadwalan proses, manajemen, dan alokasi sumber daya.
Ini adalah perbedaan penting antara suatu proses dan utas.
2. Siklus hidup utas dan lima status dasar
Benang Java memiliki lima status dasar:
(1) status baru (baru): Ketika pasangan objek utas dibuat, ia memasuki keadaan baru, seperti: thread t = myThread baru ();
(2) Status siap (runnable): Ketika metode start () dari objek utas (t.start ();), utas memasuki keadaan siap. Sebuah utas dalam keadaan siap hanya berarti bahwa utas sudah siap dan sedang menunggu CPU untuk menjadwalkan eksekusi kapan saja, bukan berarti utas akan dieksekusi segera setelah t.start () dieksekusi;
(3) Status berjalan: Ketika CPU mulai menjadwalkan utas dalam keadaan siap, utas dapat benar -benar dieksekusi, yaitu, ia memasuki keadaan berjalan. Catatan: Keadaan siap adalah satu -satunya entri ke keadaan berjalan, yaitu, jika utas ingin memasuki status berjalan untuk dieksekusi, pertama -tama harus dalam keadaan siap;
(4) Status yang diblokir: Untuk beberapa alasan, utas dalam keadaan berjalan sementara menyerah penggunaan CPU dan menghentikan eksekusi. Pada saat ini, ia memasuki keadaan pemblokiran. Tidak akan memiliki kesempatan untuk dipanggil oleh CPU lagi untuk memasuki keadaan berjalan. Menurut alasan pemblokiran, status pemblokiran dapat dibagi menjadi tiga jenis:
① Menunggu untuk memblokir: utas dalam keadaan berjalan mengeksekusi metode tunggu () untuk membuat utas memasuki status pemblokiran menunggu;
② Pemblokiran yang disinkronkan: Utas gagal untuk mendapatkan kunci sinkronisasi yang disinkronkan (karena kunci ditempati oleh utas lain), dan akan memasuki keadaan pemblokiran yang disinkronkan;
③ BLOCKING LAIN: Saat memanggil utas tidur () atau bergabung () atau mengirim permintaan I/O, utas akan memasukkan status pemblokiran. Ketika status tidur () diatur waktu, bergabung () menunggu utas untuk diakhiri atau diatur waktunya, atau pemrosesan I/O selesai, utas yang dimasukkan kembali ke keadaan siap.
(5) Status Mati: Utas telah selesai mengeksekusi atau keluar dari metode run () karena pengecualian, dan utas mengakhiri siklus hidupnya.
3. Implementasi Java Multithreading
Di Java, jika Anda ingin mengimplementasikan program multi-threaded, Anda harus mengandalkan kelas utama utas (seperti konsep kelas utama, yang mewakili kelas utama utas), tetapi kelas utama utas ini perlu memiliki beberapa persyaratan khusus saat mendefinisikannya. Kelas ini dapat mewarisi kelas utas atau mengimplementasikan antarmuka Runnable untuk menyelesaikan definisi.
1. Mewarahkan kelas utas untuk mengimplementasikan multi-threading
java.lang.thread adalah kelas yang bertanggung jawab untuk operasi utas. Kelas apa pun dapat menjadi kelas utama utas jika mewarisi kelas utas. Karena ini adalah kelas utama, ia harus memiliki metode penggunaannya, dan metode utama yang dimulai oleh utas perlu menimpa metode run () di kelas utas.
Tentukan kelas tubuh utas:
kelas mythread memperluas utas {// kelas utama judul string private thread; public mythread (judul string) {this.title = title; } @Override public void run () {// Metode utama utas untuk (int x = 0; x <10; x ++) {System.out.println (this.title + "run, x =" + x); }}}Sekarang ada kelas utas dan ada metode operasi yang sesuai di dalamnya, objek harus dihasilkan dan metode di dalamnya harus dipanggil, sehingga program berikut ditulis:
Public Class TestDemo {public static void main (string [] args) {mythread mt1 = mythread baru ("thread a"); Mythread mt2 = mythread baru ("thread b"); Mythread mt3 = mythread baru ("thread c"); mt1.run (); mt2.run (); mt3.run (); }Hasil Menjalankan:
Thread a runs, x = 0
Thread a runs, x = 1
Thread a runs, x = 2
Thread a runs, x = 3
Thread a runs, x = 4
Thread a run, x = 5
Thread a runs, x = 6
Thread a runs, x = 7
Thread a runs, x = 8
Thread a runs, x = 9
Thread B berjalan, x = 0
Thread B berjalan, x = 1
Thread B berjalan, x = 2
Thread B berjalan, x = 3
Thread B berjalan, x = 4
Thread B berjalan, x = 5
Thread B berjalan, x = 6
Thread B berjalan, x = 7
Thread B berjalan, x = 8
Thread B berjalan, x = 9
Thread C berjalan, x = 0
Thread C berjalan, x = 1
Thread C berjalan, x = 2
Thread C berjalan, x = 3
Thread C berjalan, x = 4
Thread C berjalan, x = 5
Thread C berjalan, x = 6
Thread C berjalan, x = 7
Thread C berjalan, x = 8
Thread C berjalan, x = 9
Kami menemukan bahwa operasi di atas tidak benar-benar memulai multi-threading, karena pelaksanaan beberapa utas harus dijalankan secara bergantian, dan pada saat ini dieksekusi secara berurutan, dan kode masing-masing objek akan terus dieksekusi ke bawah setelah kode masing-masing objek dieksekusi.
Jika Anda ingin benar-benar memulai multi-threading dalam suatu program, Anda harus mengandalkan metode kelas utas: public void start (), yang berarti Anda benar-benar memulai multi-threading. Setelah memanggil metode ini, metode run () akan secara tidak langsung disebut:
Public Class TestDemo {public static void main (string [] args) {mythread mt1 = mythread baru ("thread a"); Mythread mt2 = mythread baru ("thread b"); Mythread mt3 = mythread baru ("thread c"); mt1.start (); mt2.start (); mt3.start (); }}Hasil Menjalankan:
Thread C berjalan, x = 0
Thread a runs, x = 0
Thread B berjalan, x = 0
Thread a runs, x = 1
Thread C berjalan, x = 1
Thread a runs, x = 2
Thread B berjalan, x = 1
Thread a runs, x = 3
Thread a runs, x = 4
Thread a run, x = 5
Thread C berjalan, x = 2
Thread C berjalan, x = 3
Thread C berjalan, x = 4
Thread C berjalan, x = 5
Thread C berjalan, x = 6
Thread C berjalan, x = 7
Thread C berjalan, x = 8
Thread C berjalan, x = 9
Thread a runs, x = 6
Thread a runs, x = 7
Thread a runs, x = 8
Thread a runs, x = 9
Thread B berjalan, x = 2
Thread B berjalan, x = 3
Thread B berjalan, x = 4
Thread B berjalan, x = 5
Thread B berjalan, x = 6
Thread B berjalan, x = 7
Thread B berjalan, x = 8
Thread B berjalan, x = 9
Pada saat ini, Anda dapat menemukan bahwa beberapa utas dieksekusi secara bergantian satu sama lain, tetapi hasil dari setiap eksekusi berbeda. Melalui kode di atas, kami dapat menarik kesimpulan: Jika Anda ingin memulai utas, Anda harus mengandalkan metode start () dari kelas utas untuk dieksekusi. Setelah utas dimulai, metode run () akan dipanggil secara default.
Setelah menelepon metode start (), serangkaian hal -hal rumit terjadi:
(1) Mulai utas eksekusi baru (dengan tumpukan panggilan baru);
(2) utas ditransfer dari keadaan baru ke negara yang dapat dilalui;
(3) Ketika utas mendapat kesempatan untuk mengeksekusi, metode target run () akan berjalan.
CATATAN: Untuk Java, metode run () tidak ada yang istimewa. Seperti metode utama (), itu hanya berarti bahwa utas baru mengetahui nama metode (dan tanda tangan) panggilan. Oleh karena itu, legal untuk memanggil metode RUN pada runnable atau utas, tetapi tidak memulai utas baru.
Penjelasan: Mengapa Anda harus menelepon start () alih -alih langsung memanggil run () saat utas dimulai?
Kami menemukan bahwa setelah menelepon start (), itu sebenarnya mengeksekusi metode run () yang ditimpa, jadi mengapa tidak memanggil metode run () secara langsung? Untuk menjelaskan masalah ini, buka kode sumber kelas utas dan amati definisi metode start ():
void start publik yang disinkronkan () {if (threadStatus! = 0) Lempar baru ilegalthreadStateException (); group.add (ini); Boolean dimulai = false; coba {start0 (); dimulai = true; } akhirnya {coba {if (! start) {group.threadStartFailed (this); }} catch (throwable abaikan) {}}} private void start0 ();Buka kode sumber metode ini dan Anda akan menemukan bahwa metode ini akan melempar pengecualian "ilegalthreadStateException". Secara umum, jika suatu metode menggunakan lemparan untuk melempar objek pengecualian, maka pengecualian ini harus ditangkap menggunakan mencoba ... tangkap, atau dilemparkan menggunakan lemparan pada deklarasi metode, tetapi tidak ada di area ini. Mengapa? Karena kelas pengecualian ini termasuk dalam subkelas pengecualian runtime (runtimeException):
java.lang.Object
|- Java.lang.Bertible
|- Java.lang.Exception
|- java.lang.runtimeException
|- java.lang.illegalargumentException
|- java.lang.illegalthreadStateException
Pengecualian ini akan dilemparkan ketika objek utas berulang kali dimulai, yaitu objek utas hanya dapat dimulai sekali.
Salah satu bagian paling penting dari metode Start () adalah metode Start0 (), dan metode ini menggunakan definisi kata kunci asli.
Kata kunci asli mengacu pada antarmuka asli Java, yaitu Java digunakan untuk memanggil fungsi fungsi sistem operasi asli untuk menyelesaikan beberapa operasi khusus. Pengembangan kode semacam itu hampir jarang terlihat di Java karena fitur terbesar Java adalah portabilitas. Jika suatu program hanya dapat digunakan pada sistem operasi tetap, portabilitas akan sepenuhnya hilang, sehingga operasi ini umumnya tidak digunakan.
Implementasi multithreading harus memerlukan dukungan dari sistem operasi. Maka metode start0 () di atas sebenarnya sangat mirip dengan metode abstrak tanpa metode metode. Badan metode ini diserahkan kepada JVM untuk diimplementasikan, yaitu, JVM di Windows dapat menggunakan metode A untuk mengimplementasikan start0 (), sedangkan JVM di Linux dapat menggunakan metode B untuk mengimplementasikan start0 (), tetapi ketika memanggil, tidak akan peduli dengan metode spesifik untuk mengimplementasikan metode start0 (), tetapi hanya peduli tentang hasil operasi akhir, dan diberikan kepada JV.
Oleh karena itu, dalam operasi multi-threaded, menggunakan metode start () untuk memulai operasi multi-threaded membutuhkan panggilan fungsi sistem operasi.
2. Menerapkan antarmuka runnable untuk mengimplementasikan multi-threading
Menggunakan kelas utas memang dapat memfasilitasi implementasi multi-threading, tetapi kerugian terbesar dari metode ini adalah masalah warisan tunggal. Untuk tujuan ini, antarmuka runnable juga dapat digunakan di Java untuk mengimplementasikan multi-threading. Definisi antarmuka ini adalah sebagai berikut:
antarmuka publik runnable {public void run ();}Menerapkan multi-threading melalui antarmuka runnable:
kelas myThread mengimplementasikan runnable {// judul string private class utama utas; public mythread (judul string) {this.title = title; } @Override public void run () {// Metode utama utas untuk (int x = 0; x <10; x ++) {System.out.println (this.title + "run, x =" + x); }}}Ini tidak jauh berbeda dari cara sebelumnya mewarisi kelas utas, tetapi satu keuntungan adalah menghindari keterbatasan warisan tunggal.
Tapi masalahnya ada di sini. Seperti yang disebutkan sebelumnya, jika Anda ingin memulai multi-threading, Anda harus mengandalkan metode start () dari kelas utas. Saat Anda mewarisi kelas utas, Anda dapat secara langsung mewarisi metode ini dan menggunakannya. Tetapi sekarang Anda menerapkan antarmuka yang dapat dijalankan. Tanpa metode ini, Anda dapat mewarisi itu. Apa yang harus kamu lakukan?
Untuk mengatasi masalah ini, Anda masih perlu mengandalkan kelas utas untuk menyelesaikannya. Konstruktor didefinisikan di kelas utas untuk menerima objek antarmuka runnable:
utas publik (target runnable);
Mulai Multithreading Menggunakan Kelas Thread:
Public Class TestDemo {public static void main (String [] args) melempar Exception {mythread mt1 = myThread baru ("Thread a"); Mythread mt2 = mythread baru ("thread b"); Mythread mt3 = mythread baru ("thread c"); utas baru (mt1) .start (); utas baru (mt2) .start (); utas baru (mt3) .start (); }}Hasil Menjalankan:
Thread a runs, x = 0
Thread B berjalan, x = 0
Thread B berjalan, x = 1
Thread C berjalan, x = 0
Thread B berjalan, x = 2
Thread a runs, x = 1
Thread B berjalan, x = 3
Thread C berjalan, x = 1
Thread C berjalan, x = 2
Thread B berjalan, x = 4
Thread B berjalan, x = 5
Thread a runs, x = 2
Thread a runs, x = 3
Thread a runs, x = 4
Thread a run, x = 5
Thread a runs, x = 6
Thread a runs, x = 7
Thread a runs, x = 8
Thread a runs, x = 9
Thread B berjalan, x = 6
Thread B berjalan, x = 7
Thread B berjalan, x = 8
Thread B berjalan, x = 9
Thread C berjalan, x = 3
Thread C berjalan, x = 4
Thread C berjalan, x = 5
Thread C berjalan, x = 6
Thread C berjalan, x = 7
Thread C berjalan, x = 8
Thread C berjalan, x = 9
Pada saat ini, tidak hanya startup multi-threaded dicapai, tetapi juga tidak ada batasan warisan tunggal.
3. Metode ketiga untuk mengimplementasikan multi-threading: Gunakan antarmuka yang dapat dipanggil untuk mengimplementasikan multi-threading
Multithreading menggunakan antarmuka runnable dapat menghindari keterbatasan warisan tunggal, tetapi ada masalah bahwa metode run () dalam antarmuka runnable tidak dapat mengembalikan hasil operasi. Untuk mengatasi masalah ini, antarmuka baru disediakan: antarmuka yang dapat dipanggil (java.util.concurrent.callable).
Antarmuka publik Callable <V> {public v call () melempar pengecualian;}Setelah menjalankan metode Call () di antarmuka yang dapat dipanggil, hasilnya akan dikembalikan. Jenis hasil yang dikembalikan ditentukan oleh obat generik pada antarmuka yang dapat dipanggil.
Operasi spesifik menerapkan antarmuka yang dapat dipanggil untuk mengimplementasikan multi-threading adalah:
Buat kelas implementasi antarmuka yang dapat dipanggil dan mengimplementasikan metode clall (); Kemudian gunakan kelas Futuretask untuk membungkus objek kelas implementasi yang dapat dipanggil, dan gunakan objek Futuretask ini sebagai target objek utas untuk membuat utas.
Tentukan kelas tubuh utas:
Impor java.util.concurrent.callable; kelas MyThread mengimplementasikan Callable <string> {private int ticket = 10; @Override public string call () melempar pengecualian {untuk (int i = 0; i <20; i ++) {if (this.ticket> 0) {System.out.println ("Jual tiket, sisa jumlah suara adalah"+this.ticket -); }} return "Tiket telah terjual habis"; }}Kelas utas tidak secara langsung mendukung antarmuka yang dapat dipanggil. Setelah JDK1.5, kelas disediakan:
java.util.concurrent.futuretask <v>
Kelas ini terutama bertanggung jawab atas pengoperasian objek antarmuka yang dapat dipanggil. Struktur definisi adalah sebagai berikut:
Futuretask kelas publik <v>
memperluas objek
implement runnableFurture <V>
Antarmuka RunnableFurture memiliki definisi berikut:
Antarmuka Publik RunnableFurture <V>
memperluas runnable, Future <v>
Konstruktor berikut didefinisikan dalam kelas Futuretask:
Futuretask Publik (Callable <V> Callable)
Sekarang, Anda akhirnya dapat menerima objek antarmuka yang dapat dipanggil melalui kelas Futuretask. Tujuan penerimaan adalah untuk mendapatkan hasil pengembalian dari metode panggilan ().
Dari analisis di atas kita dapat menemukan:
Kelas Futuretask dapat menerima objek antarmuka yang dapat dipanggil, dan kelas Futuretask mengimplementasikan antarmuka RunnableFurture, yang mewarisi antarmuka yang dapat dilalui.
Jadi, kita bisa memulai multithreading seperti ini:
kelas publik testDemo {public static void main (string [] args) melempar Exception {mythread mt1 = myThread baru (); Mythread mt2 = mythread baru (); Futuretask <string> Tugas1 = Metode FuturEtask baru <String> (mt1); // Dapatkan panggilan () Metode untuk mengembalikan hasil Futuretask <string> Tugas2 = Metode Futuretask baru <string> (mt2); // Dapatkan metode panggilan () untuk mengembalikan hasil // Futuretask adalah subkelas antarmuka yang dapat dijalankan. Anda dapat menggunakan konstruksi kelas utas untuk menerima objek tugas utas baru (Task1) .start (); utas baru (Task2) .start (); // Setelah eksekusi multithread selesai, Anda dapat menggunakan metode GET () di masa depan antarmuka induk Futuretask untuk mendapatkan hasil eksekusi System.out.println ("Hasil Pengembalian Thread 1:"+Task1.get ()); System.out.println ("Hasil Pengembalian Thread 2:"+Task2.get ()); }}Hasil Menjalankan:
Jual tiket, sisa jumlah suara adalah 10
Jual tiket, sisa jumlah suara adalah 10
Jual tiket, sisa jumlah suara adalah 9
Jual tiket, sisa jumlah suara adalah 8
Jual tiket, sisa jumlah suara adalah 7
Jual tiket, sisa jumlah suara adalah 9
Jual tiket, sisa jumlah suara adalah 6
Jual tiket, sisa jumlah suara adalah 8
Jual tiket, sisa jumlah suara adalah 5
Jual tiket, sisa jumlah suara adalah 7
Jual tiket, sisa jumlah suara adalah 4
Jual tiket, sisa jumlah suara adalah 6
Jual tiket, sisa jumlah suara adalah 3
Jual tiket, sisa jumlah suara adalah 5
Jual tiket, sisa jumlah suara adalah 2
Jual tiket, sisa jumlah suara adalah 4
Jual tiket, sisa jumlah suara adalah 1
Jual tiket, sisa jumlah suara adalah 3
Jual tiket, sisa jumlah suara adalah 2
Jual tiket, sisa jumlah suara adalah 1
Hasil pengembalian Thread 1: Tiket telah terjual habis. Hasil pengembalian Thread 2: Tiket telah terjual habis.
ringkasan:
Di atas menjelaskan tiga cara untuk mengimplementasikan multithreading. Untuk startup utas, mereka semua disebut metode start () dari objek utas. Penting untuk dicatat bahwa metode start () tidak dapat dipanggil dua kali pada objek utas yang sama.