Tutorial Java mengatakan bahwa membuat Thread adalah mahal. Tapi mengapa itu mahal? Apa sebenarnya yang terjadi ketika Java Thread dibuat yang membuat pembuatannya mahal? Saya menganggap pernyataan itu benar, tetapi saya hanya tertarik pada mekanisme pembuatan Thread di JVM.
Overhead siklus hidup ulir. Pembuatan dan penguraian benang tidak gratis. Overhead aktual bervariasi di seluruh platform, tetapi pembuatan utas membutuhkan waktu, memperkenalkan latensi ke dalam pemrosesan permintaan, dan memerlukan beberapa aktivitas pemrosesan oleh JVM dan OS. Jika permintaan sering dan ringan, seperti di sebagian besar aplikasi server, membuat utas baru untuk setiap permintaan dapat menggunakan sumber daya komputasi yang signifikan.
Dari Java Concurrency in Practice
Oleh Brian Goetz, Tim Peierls, Joshua Bloch, Joseph Bowbeer, David Holmes, Doug Lea
Cetak ISBN-10: 0-321-34960-1
sumber
Jawaban:
Pembuatan utas Java mahal karena ada sedikit pekerjaan yang harus dilakukan:
Ini juga mahal dalam arti bahwa utas mengikat sumber daya selama masih hidup; misal tumpukan ulir, benda apa pun yang dapat dijangkau dari tumpukan, penjelas utas JVM, penjelas utas asli OS.
Biaya semua hal ini adalah platform spesifik, tetapi tidak murah untuk platform Java apa pun yang pernah saya temui.
Pencarian Google menemukan saya patokan lama yang melaporkan tingkat pembuatan utas ~ 4000 per detik pada Sun Java 1.4.1 pada prosesor ganda 2002 Xeon yang menjalankan Linux kuno 2002. Platform yang lebih modern akan memberikan angka yang lebih baik ... dan saya tidak bisa mengomentari metodologi ... tapi setidaknya itu memberikan gambaran kasar tentang seberapa mahal kemungkinan pembuatan utas.
Benchmark Peter Lawrey menunjukkan bahwa pembuatan utas secara signifikan lebih cepat akhir-akhir ini dalam hal absolut, tetapi tidak jelas berapa banyak dari ini karena peningkatan di Jawa dan / atau OS ... atau kecepatan prosesor yang lebih tinggi. Tetapi angka-angkanya masih menunjukkan peningkatan 150+ lipat jika Anda menggunakan kumpulan utas versus membuat / memulai utas baru setiap kali. (Dan dia menekankan bahwa ini semua relatif ...)
(Di atas mengasumsikan "utas asli" daripada "utas hijau", tetapi JVM modern semua menggunakan utas asli untuk alasan kinerja. Utas hijau mungkin lebih murah untuk dibuat, tetapi Anda membayarnya di area lain.)
Saya telah melakukan sedikit penggalian untuk melihat bagaimana tumpukan Java thread benar-benar dialokasikan. Dalam kasus OpenJDK 6 di Linux, tumpukan thread dialokasikan oleh panggilan ke
pthread_create
yang membuat utas asli. (JVM tidak melewatipthread_create
tumpukan yang telah dialokasikan sebelumnya.)Kemudian, di
pthread_create
dalam stack dialokasikan oleh panggilan kemmap
sebagai berikut:Menurutnya
man mmap
,MAP_ANONYMOUS
flag menyebabkan memori diinisialisasi ke nol.Dengan demikian, meskipun mungkin tidak penting bahwa tumpukan ulir Java baru dipusatkan (sesuai spesifikasi JVM), dalam praktiknya (setidaknya dengan OpenJDK 6 di Linux), tumpukan ini nol.
sumber
malloc()
fungsi C standar , yang mungkin digunakan JVM dengan baik, tidak menjamin bahwa memori yang dialokasikan nol-ed (mungkin untuk menghindari masalah kinerja seperti itu saja).mmap()
panggilan tersebut dipetakan secara copy-on-write ke halaman nol, sehingga initailisasinya terjadi bukan di dalammmap()
dirinya sendiri, tetapi ketika halaman pertama kali ditulis , dan kemudian hanya satu halaman pada saat itu. sebuah waktu. Yaitu, ketika utas memulai eksekusi, dengan biaya ditanggung oleh utas yang dibuat daripada utas pencipta.Yang lain telah mendiskusikan dari mana biaya threading berasal. Jawaban ini mencakup mengapa membuat utas tidak semahal dibandingkan banyak operasi, tetapi relatif mahal dibandingkan dengan alternatif pelaksanaan tugas, yang relatif lebih murah.
Alternatif yang paling jelas untuk menjalankan tugas di utas lain adalah menjalankan tugas di utas yang sama. Ini sulit dipahami oleh mereka yang beranggapan bahwa lebih banyak utas selalu lebih baik. Logikanya adalah jika overhead menambahkan tugas ke utas lain lebih besar dari waktu yang Anda simpan, bisa lebih cepat untuk melakukan tugas di utas saat ini.
Alternatif lain adalah menggunakan thread pool. Kumpulan utas dapat lebih efisien karena dua alasan. 1) menggunakan kembali utas yang sudah dibuat. 2) Anda dapat menyetel / mengontrol jumlah utas untuk memastikan Anda memiliki kinerja optimal.
Program berikut mencetak ....
Ini adalah tes untuk tugas sepele yang memperlihatkan overhead dari setiap opsi threading. (Tugas pengujian ini adalah jenis tugas yang sebenarnya paling baik dilakukan di utas saat ini.)
Seperti yang Anda lihat, membuat utas baru hanya berharga ~ 70 μs. Ini bisa dianggap sepele di banyak, jika tidak sebagian besar, kasus penggunaan. Secara relatif, ini lebih mahal daripada alternatifnya dan untuk beberapa situasi, thread thread atau tidak menggunakan thread sama sekali adalah solusi yang lebih baik.
sumber
Secara teori, ini tergantung pada JVM. Dalam praktiknya, setiap utas memiliki jumlah memori tumpukan yang relatif besar (256 KB per default, saya kira). Selain itu, utas diimplementasikan sebagai utas OS, sehingga membuatnya melibatkan panggilan OS, yaitu saklar konteks.
Sadarilah bahwa "mahal" dalam komputasi selalu sangat relatif. Penciptaan utas sangat mahal relatif terhadap penciptaan sebagian besar objek, tetapi tidak terlalu mahal relatif terhadap pencarian harddisk acak. Anda tidak harus menghindari membuat utas dengan cara apa pun, tetapi membuat ratusannya per detik bukanlah langkah yang cerdas. Dalam kebanyakan kasus, jika desain Anda membutuhkan banyak utas, Anda harus menggunakan kumpulan utas ukuran terbatas.
sumber
K
= 1024 dank
= 1000.;) en.wikipedia.org/wiki/KibibyteAda dua jenis utas:
Thread yang tepat : ini adalah abstraksi di sekitar fasilitas threading sistem operasi yang mendasarinya. Karenanya, pembuatan utas sama mahalnya dengan sistem - selalu ada overhead.
Thread "Hijau" : dibuat dan dijadwalkan oleh JVM, ini lebih murah, tetapi tidak terjadi paralellisme yang tepat. Ini berperilaku seperti utas, tetapi dieksekusi dalam utas JVM di OS. Mereka tidak sering digunakan, setahu saya.
Faktor terbesar yang dapat saya pikirkan dalam overhead pembuatan thread, adalah ukuran stack yang telah Anda tentukan untuk thread Anda. Thread stack-size dapat dilewatkan sebagai parameter saat menjalankan VM.
Selain itu, pembuatan utas sebagian besar bergantung pada OS, dan bahkan tergantung pada implementasi VM.
Sekarang, izinkan saya menunjukkan sesuatu: membuat utas mahal jika Anda berencana menembakkan 2.000 utas per detik, setiap detik dari runtime Anda. JVM tidak dirancang untuk mengatasinya . Jika Anda akan memiliki beberapa pekerja stabil yang tidak akan dipecat dan dibunuh berulang kali, santai saja.
sumber
Menciptakan
Threads
membutuhkan alokasi memori yang cukup karena harus membuat bukan hanya satu, tetapi dua tumpukan baru (satu untuk kode java, satu untuk kode asli). Penggunaan Executors / Thread Pools dapat menghindari overhead, dengan menggunakan kembali utas untuk beberapa tugas untuk Pelaksana .sumber
Jelas inti masalahnya adalah apa yang dimaksud dengan 'mahal'.
Utas perlu membuat tumpukan dan menginisialisasi tumpukan berdasarkan metode jalankan.
Itu perlu mengatur struktur status kontrol, yaitu keadaan apa yang bisa dijalankan, menunggu dll.
Mungkin ada banyak sinkronisasi di sekitar pengaturan hal-hal ini.
sumber