Apa kegunaan utama yield (), dan apa perbedaannya dengan join () dan interrupt ()?


106

Saya sedikit bingung tentang penggunaan yield()metode di Java, khususnya pada contoh kode di bawah ini. Saya juga membaca bahwa yield () 'digunakan untuk mencegah eksekusi thread'.

Pertanyaan saya adalah:

  1. Saya percaya kode di bawah ini menghasilkan output yang sama baik saat menggunakan yield()maupun saat tidak menggunakannya. Apakah ini benar?

  2. Sebenarnya, apa kegunaan utama dari yield()?

  3. Dalam hal apa yang yield()berbeda dari metode join()dan interrupt()?

Contoh kode:

public class MyRunnable implements Runnable {

   public static void main(String[] args) {
      Thread t = new Thread(new MyRunnable());
      t.start();

      for(int i=0; i<5; i++) {
          System.out.println("Inside main");
      }
   }

   public void run() {
      for(int i=0; i<5; i++) {
          System.out.println("Inside run");
          Thread.yield();
      }
   }
}

Saya mendapatkan output yang sama menggunakan kode di atas dengan dan tanpa menggunakan yield():

Inside main
Inside main
Inside main
Inside main
Inside main
Inside run
Inside run
Inside run
Inside run
Inside run

Qustion ini harus ditutup karena terlalu luas .
Raedwald

Tidak, itu tidak mengembalikan hasil yang sama ketika Anda punya yield()dan tidak. ketika Anda memiliki i besar daripada 5, Anda dapat melihat efek yield()metode.
lakshman

Jawaban:


97

Sumber: http://www.javamex.com/tutorials/threads/yield.shtml

Windows

Dalam implementasi Hotspot, cara Thread.yield()kerjanya telah berubah antara Java 5 dan Java 6.

Di Java 5, Thread.yield()memanggil panggilan Windows API Sleep(0). Ini memiliki efek khusus untuk membersihkan kuantum utas saat ini dan meletakkannya di akhir antrean untuk tingkat prioritasnya . Dengan kata lain, semua utas yang dapat dijalankan dengan prioritas yang sama (dan yang berprioritas lebih besar) akan mendapat kesempatan untuk dijalankan sebelum utas yang dihasilkan diberikan waktu CPU berikutnya. Ketika akhirnya dijadwalkan ulang, ia akan kembali dengan kuantum penuh penuh , tetapi tidak "membawa" salah satu kuantum yang tersisa sejak saat pelepasan. Perilaku ini sedikit berbeda dari tidur bukan-nol di mana benang tidur umumnya kehilangan 1 nilai kuantum (efeknya, 1/3 dari tanda 10 atau 15 md).

Di Jawa 6, perilaku ini diubah. VM Hotspot sekarang diimplementasikan Thread.yield()menggunakan SwitchToThread()panggilan API Windows . Panggilan ini membuat utas saat ini melepaskan pembagian waktu saat ini , tetapi tidak seluruh kuantumnya. Ini berarti bahwa bergantung pada prioritas utas lainnya, utas hasil dapat dijadwalkan kembali dalam satu periode interupsi nanti . (Lihat bagian tentang penjadwalan utas untuk informasi lebih lanjut tentang irisan waktu.)

Linux

Di Linux, Hotspot hanya memanggil sched_yield(). Konsekuensi dari panggilan ini sedikit berbeda, dan mungkin lebih parah daripada di bawah Windows:

  • utas yang dihasilkan tidak akan mendapatkan bagian lain dari CPU sampai semua utas lainnya memiliki potongan CPU ;
  • (setidaknya di kernel 2.6.8 dan seterusnya), fakta bahwa utas telah menghasilkan secara implisit diperhitungkan oleh heuristik penjadwal pada alokasi CPU terbarunya— jadi, secara implisit, utas yang telah menghasilkan dapat diberikan lebih banyak CPU saat dijadwalkan di masa depan.

(Lihat bagian penjadwalan utas untuk detail selengkapnya tentang prioritas dan algoritme penjadwalan.)

Kapan digunakan yield()?

Saya akan mengatakan secara praktis tidak pernah . Perilakunya tidak ditentukan secara standar dan umumnya ada cara yang lebih baik untuk melakukan tugas yang mungkin ingin Anda lakukan dengan yield ():

  • jika Anda mencoba menggunakan hanya sebagian dari CPU , Anda dapat melakukan ini dengan cara yang lebih terkontrol dengan memperkirakan berapa banyak CPU yang digunakan utas dalam pemrosesan terakhirnya, lalu tidur selama beberapa waktu untuk mengimbanginya: lihat metode sleep () ;
  • jika Anda menunggu proses atau sumber daya selesai atau tersedia, ada cara yang lebih efisien untuk melakukannya, seperti dengan menggunakan join () untuk menunggu utas lain selesai, menggunakan mekanisme tunggu / beri tahu untuk mengizinkan satu utas untuk memberi sinyal kepada orang lain bahwa suatu tugas telah selesai, atau idealnya dengan menggunakan salah satu konstruksi konkurensi Java 5 seperti Semaphore atau antrean pemblokiran .

18
"sisa kuantum", "seluruh kuantum" - di suatu tempat di sepanjang jalan seseorang lupa apa arti kata "kuantum"
kbolino

@kbolino Quantum adalah atom baru.
Evgeni Sergeev

2
@kbolino - ... latin: "sebanyak", "berapa banyak" . Saya tidak melihat bagaimana hal ini bertentangan dengan penggunaan di atas. Kata itu secara sederhana berarti jumlah yang dijelaskan dari sesuatu, jadi membaginya menjadi bagian bekas dan sisa tampaknya masuk akal bagi saya.
Periata Breatta

@PeriataBreatta Saya rasa lebih masuk akal jika Anda terbiasa dengan kata di luar fisika. Definisi fisika adalah satu-satunya yang saya tahu.
kbolino

Saya memberi hadiah pada pertanyaan ini untuk mendapatkan jawaban ini diperbarui untuk 7, 8, 9. Edit dengan informasi terkini tentang 7,8 dan 8 dan Anda akan mendapatkan hadiahnya.

40

Saya melihat pertanyaan itu telah diaktifkan kembali dengan hadiah, sekarang menanyakan untuk apa kegunaan praktisnya yield. Saya akan memberikan contoh dari pengalaman saya.

Seperti yang kita ketahui, yieldmemaksa thread pemanggil untuk melepaskan prosesor tempat ia berjalan sehingga thread lain dapat dijadwalkan untuk berjalan. Ini berguna ketika utas saat ini telah menyelesaikan tugasnya untuk saat ini tetapi ingin segera kembali ke depan antrean dan memeriksa apakah beberapa kondisi telah berubah. Apa yang membedakannya dari variabel kondisi? yieldmemungkinkan utas kembali lebih cepat ke status berjalan. Saat menunggu pada variabel kondisi, utas ditangguhkan dan perlu menunggu utas yang berbeda untuk memberi sinyal bahwa itu harus dilanjutkan.yieldpada dasarnya mengatakan "izinkan utas yang berbeda untuk berjalan, tetapi izinkan saya untuk kembali bekerja segera karena saya mengharapkan sesuatu untuk berubah dalam keadaan saya dengan sangat cepat". Ini mengisyaratkan pemintalan yang sibuk, di mana suatu kondisi dapat berubah dengan cepat tetapi menangguhkan utas akan menyebabkan kinerja yang buruk.

Tapi cukup mengoceh, inilah contoh konkretnya: pola paralel muka gelombang. Contoh dasar dari masalah ini adalah menghitung "pulau" individu 1s dalam larik dua dimensi yang diisi dengan 0s dan 1s. Sebuah "pulau" adalah sekumpulan sel yang berdekatan satu sama lain baik secara vertikal maupun horizontal:

1 0 0 0
1 1 0 0
0 0 0 1
0 0 1 1
0 0 1 1

Di sini kita memiliki dua pulau 1: kiri atas dan kanan bawah.

Solusi sederhananya adalah dengan membuat lintasan pertama di seluruh larik dan mengganti 1 nilai dengan penghitung tambahan sehingga pada akhirnya setiap 1 diganti dengan nomor urutnya dalam urutan mayor baris:

1 0 0 0
2 3 0 0
0 0 0 4
0 0 5 6
0 0 7 8

Pada langkah berikutnya, setiap nilai diganti dengan nilai minimum antara nilai itu sendiri dan nilai tetangganya:

1 0 0 0
1 1 0 0
0 0 0 4
0 0 4 4
0 0 4 4

Sekarang kita dapat dengan mudah menentukan bahwa kita memiliki dua pulau.

Bagian yang ingin kita jalankan secara paralel adalah langkah di mana kita menghitung minimum. Tanpa membahas terlalu banyak detail, setiap utas mendapatkan baris secara berselang-seling dan bergantung pada nilai yang dihitung oleh utas yang memproses baris di atas. Oleh karena itu, setiap utas perlu sedikit tertinggal di belakang utas yang memproses baris sebelumnya, tetapi juga harus mengikuti dalam waktu yang wajar. Lebih detail dan implementasinya disajikan sendiri dalam dokumen ini . Perhatikan penggunaan sleep(0)yang lebih atau kurang C setara yield.

Dalam hal yieldini digunakan untuk memaksa setiap utas secara bergiliran menjeda, tetapi karena utas yang memproses baris yang berdekatan akan maju dengan sangat cepat untuk sementara itu, variabel kondisi akan membuktikan pilihan yang menghancurkan.

Seperti yang Anda lihat, yieldini adalah pengoptimalan butiran yang cukup halus. Penggunaannya di tempat yang salah misal menunggu pada kondisi yang jarang berubah akan menyebabkan penggunaan CPU yang berlebihan.

Maaf atas ocehan panjangnya, semoga aku membuat diriku jelas.


1
IIUC apa yang Anda sajikan dalam dokumen, idenya adalah bahwa dalam kasus ini, lebih efisien untuk sibuk-menunggu, memanggil yieldketika kondisi tidak terpenuhi untuk memberikan kesempatan utas lain untuk melanjutkan komputasi, daripada menggunakan lebih tinggi- primitif sinkronisasi tingkat, bukan?
Petr Pudlák

3
@ Petr Pudlák: Ya. Saya membandingkan ini vs. menggunakan pensinyalan utas dan perbedaan kinerja sangat besar dalam kasus ini. Karena kondisi dapat menjadi benar dengan sangat cepat (ini adalah masalah kuncinya), variabel kondisi terlalu lambat karena utas ditahan oleh OS, daripada melepaskan CPU untuk penggunaan dalam durasi yang sangat singkat yield.
Tudor

@Tudor penjelasan yang bagus!
Pengembang Marius Žilėnas

1
“Perhatikan penggunaan sleep (0) yang kurang lebih setara dengan C yield.” .. nah, jika Anda ingin tidur (0) dengan java, kenapa tidak Anda gunakan saja? Thread.sleep () adalah sesuatu yang sudah ada. Saya tidak yakin apakah jawaban ini memberikan alasan mengapa seseorang akan menggunakan Thread.yield () daripada Thread.sleep (0); Ada juga utas yang menjelaskan mengapa mereka berbeda.
eis

@eis: Thread.sleep (0) vs Thread.yield () berada di luar cakupan jawaban ini. Saya hanya menyebutkan Thread.sleep (0) untuk orang-orang yang mencari padanan dekat di C. Pertanyaannya adalah tentang penggunaan Thread.yield ().
Tudor

12

Tentang perbedaan antara yield(), interrupt()dan join()- secara umum, tidak hanya di Java:

  1. menyerah : Secara harfiah, 'menyerah' berarti melepaskan, menyerah, menyerah. Utas yang menghasilkan memberi tahu sistem operasi (atau mesin virtual, atau apa yang tidak) itu bersedia untuk membiarkan utas lain dijadwalkan sebagai gantinya. Ini menunjukkan bahwa mereka tidak melakukan sesuatu yang terlalu kritis. Ini hanya petunjuk, dan tidak dijamin akan memiliki efek apa pun.
  2. bergabung : Ketika beberapa utas 'bergabung' pada beberapa pegangan, atau token, atau entitas, semuanya menunggu sampai semua utas relevan lainnya telah menyelesaikan eksekusi (seluruhnya atau hingga gabungan yang sesuai). Itu berarti sekelompok utas semuanya telah menyelesaikan tugasnya. Kemudian masing-masing utas ini dapat dijadwalkan untuk melanjutkan pekerjaan lain, dapat menganggap semua tugas tersebut telah selesai. (Jangan bingung dengan SQL Bergabung!)
  3. interupsi : Digunakan oleh satu utas untuk 'menyodok' utas lain yang sedang tidur, atau menunggu, atau bergabung - sehingga dijadwalkan untuk terus berjalan lagi, mungkin dengan indikasi bahwa utas telah terputus. (Jangan bingung dengan interupsi perangkat keras!)

Khusus untuk Java, lihat

  1. Bergabung:

    Bagaimana cara menggunakan Thread.join? (di sini di StackOverflow)

    Kapan bergabung dengan utas?

  2. Menghasilkan:

  3. Mengganggu:

    Apakah Thread.interrupt () jahat? (di sini di StackOverflow)


Apa yang Anda maksud dengan bergabung dengan pegangan atau token? Metode wait () dan notify () ada di Object, memungkinkan pengguna untuk menunggu di Object sembarang. Tetapi join () tampaknya kurang abstrak dan perlu dipanggil pada Thread tertentu yang ingin Anda selesaikan sebelum melanjutkan ... bukan?
spaaarky21

@ spaaarky21: Maksud saya secara umum, tidak harus di Jawa. Selain itu, a wait()bukanlah gabungan, ini tentang kunci pada objek yang coba diperoleh thread pemanggil - ia menunggu hingga kunci dibebaskan oleh orang lain dan telah diperoleh oleh utas. Tweak jawaban saya sesuai.
einpoklum

10

Pertama, deskripsi sebenarnya adalah

Menyebabkan objek thread yang sedang dieksekusi berhenti sementara dan mengizinkan thread lain untuk mengeksekusi.

Sekarang, kemungkinan besar utas utama Anda akan menjalankan perulangan lima kali sebelum runmetode utas baru dijalankan, jadi semua panggilan ke yieldhanya akan terjadi setelah perulangan di utas utama dijalankan.

joinakan menghentikan utas saat ini hingga utas yang dipanggil join()selesai dieksekusi.

interruptakan mengganggu utas yang dipanggil, menyebabkan InterruptedException .

yield memungkinkan pengalihan konteks ke utas lain, jadi utas ini tidak akan menggunakan seluruh penggunaan CPU dari proses tersebut.


+1. Perhatikan juga bahwa setelah memanggil yield (), masih tidak ada jaminan bahwa thread yang sama tidak akan dipilih untuk dieksekusi lagi, mengingat kumpulan thread dengan prioritas yang sama.
Andrew Fielden

Namun, SwitchToThread()panggilan lebih baik daripada Tidur (0) dan ini seharusnya menjadi bug di Java :)
Петър Петров

4

Jawaban saat ini sudah kedaluwarsa dan memerlukan revisi mengingat perubahan terkini.

Tidak ada perbedaan praktisThread.yield() antara versi Java sejak 6 hingga 9.

TL; DR;

Kesimpulan berdasarkan kode sumber OpenJDK ( http://hg.openjdk.java.net/ ).

Jika tidak mempertimbangkan dukungan HotSpot dari probe USDT (informasi pelacakan sistem dijelaskan dalam panduan dtrace ) dan properti JVM ConvertYieldToSleepmaka kode sumber yield()hampir sama. Lihat penjelasan di bawah ini.

Jawa 9 :

Thread.yield()memanggil metode khusus OS os::naked_yield():
Di Linux:

void os::naked_yield() {
    sched_yield();
}

Di Windows:

void os::naked_yield() {
    SwitchToThread();
}

Java 8 dan sebelumnya:

Thread.yield()memanggil metode khusus OS os::yield():
Di Linux:

void os::yield() {
    sched_yield();
}

Di Windows:

void os::yield() {  os::NakedYield(); }

Seperti yang Anda lihat, Thread.yeald()di Linux identik untuk semua versi Java.
Mari kita lihat Windows os::NakedYield()dari JDK 8:

os::YieldResult os::NakedYield() {
    // Use either SwitchToThread() or Sleep(0)
    // Consider passing back the return value from SwitchToThread().
    if (os::Kernel32Dll::SwitchToThreadAvailable()) {
        return SwitchToThread() ? os::YIELD_SWITCHED : os::YIELD_NONEREADY ;
    } else {
        Sleep(0);
    }
    return os::YIELD_UNKNOWN ;
}

Perbedaan antara Java 9 dan Java 8 di pemeriksaan tambahan keberadaan metode Win32 API SwitchToThread(). Kode yang sama hadir untuk Java 6.
Kode sumber os::NakedYield()di JDK 7 sedikit berbeda tetapi memiliki perilaku yang sama:

    os::YieldResult os::NakedYield() {
    // Use either SwitchToThread() or Sleep(0)
    // Consider passing back the return value from SwitchToThread().
    // We use GetProcAddress() as ancient Win9X versions of windows doen't support SwitchToThread.
    // In that case we revert to Sleep(0).
    static volatile STTSignature stt = (STTSignature) 1 ;

    if (stt == ((STTSignature) 1)) {
        stt = (STTSignature) ::GetProcAddress (LoadLibrary ("Kernel32.dll"), "SwitchToThread") ;
        // It's OK if threads race during initialization as the operation above is idempotent.
    }
    if (stt != NULL) {
        return (*stt)() ? os::YIELD_SWITCHED : os::YIELD_NONEREADY ;
    } else {
        Sleep (0) ;
    }
    return os::YIELD_UNKNOWN ;
}

Pemeriksaan tambahan telah dibatalkan karena SwitchToThread()metode yang tersedia sejak Windows XP dan Windows Server 2003 (lihat catatan msdn ).


2

Sebenarnya, apa kegunaan utama yield ()?

Yield menyarankan kepada CPU bahwa Anda dapat menghentikan thread saat ini dan mulai menjalankan thread dengan prioritas lebih tinggi. Dengan kata lain, menetapkan nilai prioritas rendah ke utas saat ini untuk memberikan ruang bagi utas yang lebih penting.

Saya percaya kode di bawah ini menghasilkan output yang sama baik saat menggunakan yield () dan saat tidak menggunakannya. Apakah ini benar?

TIDAK, keduanya akan menghasilkan hasil yang berbeda. Tanpa yield (), setelah thread mendapatkan kontrol, thread akan menjalankan loop 'Di dalam run' sekaligus. Namun, dengan yield (), setelah thread mendapatkan kontrol, thread akan mencetak 'Proses di dalam' sekali dan kemudian akan menyerahkan kontrol ke thread lain jika ada. Jika tidak ada utas yang tertunda, utas ini akan dilanjutkan lagi. Jadi setiap kali "Inside run 'dijalankan, ia akan mencari thread lain untuk dieksekusi dan jika tidak ada thread yang tersedia, thread saat ini akan terus dijalankan.

Dalam hal apa yield () berbeda dari metode join () dan interrupt ()?

yield () adalah untuk memberikan ruang ke utas penting lainnya, join () untuk menunggu utas lain menyelesaikan eksekusinya, dan interrupt () untuk mengganggu utas yang sedang dieksekusi untuk melakukan sesuatu yang lain.


Hanya ingin memastikan apakah pernyataan ini benar Without a yield(), once the thread gets control it will execute the 'Inside run' loop in one go? Mohon klarifikasi.
Abdullah Khan

0

Thread.yield()menyebabkan utas beralih dari status "Berjalan" ke status "Dapat dijalankan". Catatan: Ini tidak menyebabkan utas menjadi status "Menunggu".


@PJMeisch, Tidak ada RUNNINGstatus untuk java.lang.Threadcontoh. Namun hal itu tidak menghalangi status "berjalan" asli untuk utas asli di mana suatu Threadinstance adalah proxy.
Solomon Slow

-1

Thread.yield ()

Ketika kita memanggil metode Thread.yield (), penjadwal thread menjaga thread yang sedang berjalan ke status Runnable dan memilih thread lain dengan prioritas yang sama atau prioritas yang lebih tinggi. Jika tidak ada utas dengan prioritas yang sama dan lebih tinggi, maka itu akan menjadwal ulang utas pemanggil yield (). Ingat metode hasil tidak membuat utas masuk ke status Menunggu atau Diblokir. Itu hanya dapat membuat utas dari Running State ke Runnable State.

Ikuti()

Ketika penggabungan dipanggil oleh sebuah instance thread, thread ini akan memberi tahu thread yang sedang mengeksekusi untuk menunggu sampai thread yang bergabung selesai. Bergabung digunakan dalam situasi ketika tugas yang harus diselesaikan sebelum tugas saat ini akan selesai.


-4

yield () utama digunakan untuk menunda aplikasi multi-threading.

semua perbedaan metode ini adalah yield () menempatkan utas ditunda saat menjalankan utas lain dan kembali setelah penyelesaian utas itu, join () akan membawa awal utas bersama-sama mengeksekusi sampai akhir dan utas lain untuk dijalankan setelah utas itu memiliki berakhir, interrupt () akan menghentikan eksekusi thread untuk sementara waktu.


Terima kasih atas jawaban Anda. Namun itu hanya mengulangi apa yang sudah dijelaskan oleh jawaban lain secara rinci. Saya menawarkan hadiah untuk kasus penggunaan yang tepat di mana yieldharus digunakan.
Petr Pudlák
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.