Rumah  >  Artikel  >  Java  >  Soalan dan Jawapan Temu Bual Pembangun Java Biasa tentang multithreading, pengumpulan sampah, kumpulan benang dan penyegerakan

Soalan dan Jawapan Temu Bual Pembangun Java Biasa tentang multithreading, pengumpulan sampah, kumpulan benang dan penyegerakan

DDD
DDDasal
2024-09-13 06:21:36640semak imbas

Common Java Developer Interview Questions and Answers on multithreading, garbage collection, thread pools, and synchronization

Kitaran Hayat dan Pengurusan Benang

Soalan: Bolehkah anda menerangkan kitaran hayat thread dalam Java dan cara keadaan thread diuruskan oleh JVM?

Jawapan:

Benang dalam Java mempunyai keadaan kitaran hayat berikut, diuruskan oleh JVM:

  1. Baharu: Apabila urutan dibuat tetapi belum bermula, ia berada dalam keadaan baharu. Ini berlaku apabila objek Thread dibuat seketika, tetapi kaedah start() belum dipanggil lagi.

  2. Boleh Dijalankan: Setelah kaedah mula() dipanggil, benang memasuki keadaan boleh dijalankan. Dalam keadaan ini, benang sedia untuk dijalankan tetapi sedang menunggu penjadual benang JVM untuk menetapkan masa CPU. Urutan juga mungkin menunggu untuk memperoleh semula CPU selepas didahulukan.

  3. Disekat: Satu utas memasuki keadaan disekat semasa menunggu kunci monitor dilepaskan. Ini berlaku apabila satu utas memegang kunci (menggunakan disegerakkan) dan satu lagi utas cuba memperolehnya.

  4. Menunggu: Satu utas memasuki keadaan menunggu apabila ia menunggu selama-lamanya untuk urutan lain untuk melaksanakan tindakan tertentu. Sebagai contoh, urutan boleh memasuki keadaan menunggu dengan memanggil kaedah seperti Object.wait(), Thread.join(), atau LockSupport.park().

  5. Menunggu Bermasa: Dalam keadaan ini, urutan menunggu untuk tempoh tertentu. Ia boleh berada dalam keadaan ini disebabkan oleh kaedah seperti Thread.sleep(), Object.wait(long timeout), atau Thread.join(long millis).

  6. Ditamatkan: Urutan memasuki keadaan ditamatkan apabila ia telah selesai dilaksanakan atau telah digugurkan. Urutan yang ditamatkan tidak boleh dimulakan semula.

Peralihan Keadaan Benang:

  • Peralihan urutan daripada baharu kepada boleh dijalankan apabila start() dipanggil.
  • Urutan boleh bergerak antara keadaan boleh dijalankan, menunggu, menunggu bermasa dan disekat sepanjang hayatnya bergantung pada penyegerakan, menunggu untuk kunci atau tamat masa.
  • Setelah kaedah run() thread selesai, thread bergerak ke keadaan ditamatkan.

JVM penjadual benang mengendalikan penukaran antara runnable thread berdasarkan keupayaan pengurusan utas sistem pengendalian asas. Ia menentukan bila dan berapa lama urutan mendapat masa CPU, biasanya menggunakan penghirisan masa atau penjadualan preemptive.


Penyegerakan Benang dan Pencegahan Kebuntuan

Soalan: Bagaimanakah Java mengendalikan penyegerakan benang, dan apakah strategi yang boleh anda gunakan untuk mengelakkan kebuntuan dalam aplikasi berbilang benang?

Jawapan:

Penyegerakan utas dalam Java dikendalikan menggunakan monitor atau kunci, yang memastikan hanya satu utas boleh mengakses bahagian kod kritikal pada satu masa. Ini biasanya dicapai menggunakan kata kunci disegerakkan atau objek Kunci daripada pakej java.util.concurrent.locks. Berikut ialah pecahan:

  1. Kaedah/Blok Disegerakkan:

    • Apabila benang memasuki kaedah atau blok yang disegerakkan, ia memperoleh kunci intrinsik (monitor) pada objek atau kelas. Urutan lain yang cuba memasuki blok disegerakkan pada objek/kelas yang sama disekat sehingga kunci dilepaskan.
    • Blok disegerakkan lebih disukai berbanding kaedah kerana ia membenarkan anda mengunci bahagian kritikal tertentu sahaja berbanding keseluruhan kaedah.
  2. ReentrantLock:

    • Java menyediakan ReentrantLock dalam java.util.concurrent.locks untuk kawalan yang lebih terperinci ke atas penguncian. Kunci ini menawarkan ciri tambahan seperti keadilan (FIFO) dan keupayaan untuk mencuba mengunci dengan tamat masa (tryLock()).
  3. Kebuntuan berlaku apabila dua atau lebih utas disekat selama-lamanya, masing-masing menunggu satu sama lain melepaskan kunci. Ini boleh berlaku jika benang A memegang kunci X dan menunggu kunci Y, manakala benang B memegang kunci Y dan menunggu kunci X.

Strategi untuk mengelakkan kebuntuan:

  • Pesanan Kunci: Sentiasa dapatkan kunci dalam susunan yang konsisten merentas semua urutan. Ini menghalang menunggu bulat. Contohnya, jika benang A dan benang B kedua-duanya perlu mengunci objek X dan Y, pastikan kedua-dua benang sentiasa mengunci X sebelum Y.
  • Tamat masa: Gunakan kaedah tryLock() dengan tamat masa dalam ReentrantLock untuk cuba mendapatkan kunci untuk tempoh tetap. Jika benang tidak dapat memperoleh kunci dalam masa, ia boleh berundur dan mencuba semula atau melakukan tindakan lain, mengelakkan kebuntuan.
  • Pengesanan Jalan Mati: Alat dan mekanisme pemantauan (cth., ThreadMXBean dalam JVM) boleh mengesan kebuntuan. Anda boleh menggunakan ThreadMXBean untuk mengesan jika mana-mana benang berada dalam keadaan buntu dengan memanggil kaedah findDeadlockedThreads().
   ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
   long[] deadlockedThreads = threadBean.findDeadlockedThreads();

Pencegahan Kunci Langsung: Pastikan urutan tidak terus berubah keadaannya tanpa membuat sebarang kemajuan dengan memastikan logik pengendalian perbalahan (seperti mengundur atau mencuba semula) dilaksanakan dengan betul.


Algoritma dan Penalaan Pengumpulan Sampah

Soalan: Bolehkah anda menerangkan algoritma pengumpulan sampah yang berbeza dalam Java dan cara anda menala pengumpul sampah JVM untuk aplikasi yang memerlukan kependaman rendah?

Jawapan:

JVM Java menyediakan berbilang algoritma pengumpulan sampah (GC), setiap satu direka untuk kes penggunaan yang berbeza. Berikut ialah gambaran keseluruhan algoritma utama:

  1. GC bersiri:

    • Menggunakan satu utas untuk kedua-dua koleksi kecil dan utama. Ia sesuai untuk aplikasi kecil dengan CPU teras tunggal. Ia tidak sesuai untuk aplikasi berkemampuan tinggi atau kependaman rendah.
  2. GC Selari (Pengumpul Laluan):

    • Menggunakan berbilang benang untuk pengumpulan sampah (kedua-dua GC kecil dan utama), menjadikannya lebih baik untuk pemprosesan. Walau bagaimanapun, ia boleh memperkenalkan jeda lama dalam aplikasi semasa kitaran GC penuh, menjadikannya tidak sesuai untuk aplikasi masa nyata atau kependaman rendah.
  3. G1 GC (Pengumpul Sampah Pertama Sampah):

    • Pengumpul berasaskan wilayah yang membahagikan timbunan kepada kawasan kecil. Ia direka untuk aplikasi yang memerlukan masa jeda yang boleh diramal. G1 cuba memenuhi matlamat masa jeda yang ditentukan pengguna dengan mengehadkan jumlah masa yang dihabiskan dalam kutipan sampah.
    • Sesuai untuk timbunan besar dengan beban kerja bercampur (kedua-dua objek pendek dan tahan lama).
    • Penalaan: Anda boleh menetapkan masa jeda maksimum yang diingini menggunakan -XX:MaxGCPauseMillis=, dan G1 akan cuba memenuhi masa jeda ini.
  4. ZGC (Pengumpul Sampah Z):

    • Pengumpul sampah pendaman rendah yang boleh mengendalikan timbunan yang sangat besar (berbilang terabait). ZGC melakukan kutipan sampah serentak tanpa jeda henti dunia (STW) yang lama. Ia memastikan bahawa jeda biasanya kurang daripada 10 milisaat, menjadikannya sesuai untuk aplikasi sensitif kependaman.
    • Penalaan: Penalaan minimum diperlukan. Anda boleh mendayakannya dengan -XX:+UseZGC. ZGC melaraskan secara automatik berdasarkan saiz timbunan dan beban kerja.
  5. Shenandoah GC:

    • Satu lagi GC kependaman rendah yang memfokuskan pada meminimumkan masa jeda walaupun dengan saiz timbunan yang besar. Seperti ZGC, Shenandoah melakukan pemindahan serentak, memastikan jeda biasanya dalam julat beberapa milisaat.
    • Penalaan: Anda boleh mendayakannya dengan -XX:+UseShenandoahGC dan memperhalusi tingkah laku menggunakan pilihan seperti -XX:ShenandoahGarbageHeuristics=adaptive.

Menala untuk Aplikasi Kependaman Rendah:

  • Gunakan GC serentak seperti ZGC atau Shenandoah untuk meminimumkan jeda.
  • Saiz Timbunan: Laraskan saiz timbunan berdasarkan jejak memori aplikasi. Timbunan bersaiz mencukupi mengurangkan kekerapan kitaran kutipan sampah. Tetapkan saiz timbunan dengan -Xms (saiz timbunan awal) dan -Xmx (saiz timbunan maksimum).
  • Jeda Matlamat Masa: Jika menggunakan G1 GC, tetapkan matlamat yang munasabah untuk masa jeda maksimum menggunakan -XX:MaxGCPauseMillis=.
  • Pantau dan Profil: Gunakan alat pemantauan JVM (cth., VisualVM, jstat, Log Pengumpulan Sampah) untuk menganalisis tingkah laku GC. Analisis metrik seperti masa jeda GC, kekerapan kitaran GC penuh dan penggunaan memori untuk memperhalusi pengumpul sampah.

Dengan memilih algoritma GC yang betul berdasarkan keperluan aplikasi anda dan melaraskan saiz timbunan serta matlamat masa jeda, anda boleh mengurus kutipan sampah dengan berkesan sambil mengekalkan prestasi kependaman rendah.


Kumpulan Benang dan Rangka Kerja Pelaksana

Soalan: Bagaimanakah rangka kerja Pelaksana meningkatkan pengurusan benang dalam Java, dan bilakah anda akan memilih jenis kumpulan benang yang berbeza?

Jawapan:

Rangka kerja Pelaksana dalam Java menyediakan abstraksi peringkat lebih tinggi untuk mengurus benang, menjadikannya lebih mudah untuk melaksanakan tugasan secara tidak segerak tanpa menguruskan penciptaan benang dan kitaran hayat secara langsung. Rangka kerja adalah sebahagian daripada pakej java.util.concurrent dan termasuk kelas seperti ExecutorService dan Executors.

  1. Faedah Rangka Kerja Pelaksana:

    • Kebolehgunaan Semula Benang: Daripada membuat urutan baharu untuk setiap tugasan, rangka kerja menggunakan kumpulan utas yang digunakan semula untuk berbilang tugas. Ini mengurangkan overhed penciptaan dan pemusnahan benang.
    • Penyerahan Tugasan: Anda boleh menyerahkan tugasan menggunakan Runnable, Callable, atau Future, dan rangka kerja mengurus pelaksanaan tugas dan perolehan semula hasil.
    • Pengurusan Benang: Pelaksana mengendalikan pengurusan urutan, seperti memulakan, menghentikan dan mengekalkan urutan hidup untuk tempoh terbiar, yang memudahkan kod aplikasi.
  2. **Jenis

Kolam Benang**:

  • Kolam Benang Tetap (Executors.newFixedThreadPool(n)):

    Mencipta kumpulan benang dengan bilangan benang tetap. Jika semua rangkaian sibuk, tugasan akan dibariskan sehingga satu rangkaian tersedia. Ini berguna apabila anda mengetahui bilangan tugasan atau ingin mengehadkan bilangan urutan serentak kepada nilai yang diketahui.

  • Kolam Benang Cache (Executors.newCachedThreadPool()):

    Mencipta kumpulan benang yang mencipta benang baharu mengikut keperluan tetapi menggunakan semula benang yang dibina sebelum ini apabila ia tersedia. Ia sesuai untuk aplikasi dengan banyak tugas jangka pendek tetapi boleh membawa kepada penciptaan benang tanpa had jika tugasan berjalan lama.

  • Pelaksana Thread Tunggal (Executors.newSingleThreadExecutor()):

    Satu utas melaksanakan tugas secara berurutan. Ini berguna apabila tugasan mesti dilaksanakan mengikut susunan, memastikan hanya satu tugasan dijalankan pada satu masa.

  • Kolam Benang Berjadual (Pelaksana.newScheduledThreadPool(n)):

    Digunakan untuk menjadualkan tugas untuk dijalankan selepas kelewatan atau secara berkala. Ia berguna untuk aplikasi yang mana tugas perlu dijadualkan atau diulang pada selang masa tetap (cth., tugas pembersihan latar belakang).

  1. Memilih Kolam Benang Yang Betul:
    • Gunakan kumpulan benang tetap apabila bilangan tugasan serentak dihadkan atau diketahui lebih awal. Ini menghalang sistem daripada ditimpa oleh terlalu banyak rangkaian.
    • Gunakan kumpulan benang cache untuk aplikasi dengan beban kerja yang tidak dapat diramalkan atau pecah. Kolam cache mengendalikan tugas jangka pendek dengan cekap tetapi boleh berkembang tanpa had jika tidak diurus dengan betul.
    • Gunakan pelaksana urutan tunggal untuk pelaksanaan tugas bersiri, memastikan hanya satu tugasan dijalankan pada satu masa.
    • Gunakan kumpulan utas berjadual untuk tugasan berkala atau pelaksanaan tugas tertunda, seperti penyegerakan data latar belakang atau pemeriksaan kesihatan.

Penutupan dan Pengurusan Sumber:

  • Sentiasa matikan pelaksana dengan betul menggunakan shutdown() atau shutdownNow() untuk melepaskan sumber apabila ia tidak diperlukan lagi.
  • shutdown() membenarkan tugasan yang sedang dijalankan selesai, manakala shutdownNow() cuba membatalkan tugas yang sedang dijalankan.

Dengan menggunakan Rangka kerja Pelaksana dan memilih kumpulan benang yang sesuai untuk beban kerja aplikasi anda, anda boleh mengurus konkurensi dengan lebih cekap, meningkatkan pengendalian tugas dan mengurangkan kerumitan pengurusan benang manual.

Atas ialah kandungan terperinci Soalan dan Jawapan Temu Bual Pembangun Java Biasa tentang multithreading, pengumpulan sampah, kumpulan benang dan penyegerakan. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn