Apakah Anda ingin di utas java yang tidak memakan memori seolah-olah mereka tidak sendiri dan tidak memperlambat? Penghargaan bagus, dan masalah ini menjawab pertanyaan ini.
Kami menjelaskan pekerjaan Project Loom pada kotak pizza! Ayo!
Semua ini dihapus dan ditulis khusus untuk Habr .
Tanda panggilan
Seberapa sering Anda melihat gambar ini di layanan web Anda: pada awalnya semuanya baik-baik saja, lalu satu juta orang Cina mendatangi Anda, layanan membuat sejuta benang dan tenggelam di neraka?

Apakah Anda ingin gambar yang bagus?

Dengan kata lain, apakah Anda ingin di utas java yang tidak memakan memori tetapi tidak dalam diri mereka sendiri dan tidak memperlambat? Penghargaan bagus, dan masalah ini menjawab pertanyaan ini.
Kami akan terlibat, pada kenyataannya, membongkar kerangka baru . Ingat bagaimana Wylsacom membongkar iPhone? Beberapa sudah tidak ingat pengulas lama, tetapi mengapa? Karena Habr adalah benteng utama, dan video berbayar, maaf, adalah sinar diare . Dalam posting ini kita akan membahas secara eksklusif dengan teknis hardcore.
Pertama, dua menit ke bola mata, penafian dan sampah lainnya, yang harus dikatakan. Anda dapat melewatkannya jika Anda terlalu malas.
Pertama-tama, semua yang dikatakan dalam video adalah pikiran pribadi saya, dan itu tidak ada hubungannya dengan majikan atau perusahaan Oracle yang mulia, pemerintah dunia kadal dan hal-hal terkutuk dalam mortir. Saya bahkan menulis ini pada jam tiga pagi sehingga jelas bahwa ini adalah inisiatif pribadi saya, sampah pribadi saya. Semua pertandingan murni acak.
Tetapi ada tujuan bonus lain. Kami terus-menerus berbicara tentang coroutine di Kotlin. Baru-baru ini ada wawancara dengan Roma Elizarov , dewa Corutin, dan dengan Pasha Finkelstein , yang akan menulis backend pada mereka. Segera akan ada wawancara dengan Andrei Breslav - yang merupakan ayah dari Kotlin. Dan di mana-mana proyek Loom disebutkan satu atau lain cara, karena itu adalah analog dari coroutine. Dan jika Anda tidak tahu apa itu Loom, Anda mungkin menjadi bodoh ketika membaca wawancara ini. Ada beberapa cowok keren, mereka membahas hal keren. Dan di sanalah Anda, dan Anda tidak bersama mereka, Anda bodoh. Ini sangat bodoh.
Jangan lakukan ini, baca apa yang ada dalam artikel ini, atau tonton video ini lebih lanjut, saya akan menjelaskan semuanya.
Jadi, apa komplikasinya. Ada pria seperti itu, Ron Presler.

Tahun lalu, ia pergi ke mailing list , mengatakan bahwa utas di Jawa payah, dan menyarankan agar ia menjalankan runtime dan memperbaikinya. Dan semua orang akan menertawakannya dan melempar batu, sial, kalau bukan karena fakta bahwa dia menulis Quasar sebelumnya, dan ini sebenarnya sangat keren. Anda bisa bersumpah di Quasar untuk waktu yang lama, tetapi tampaknya ada di sana dan itu bekerja, dan dalam gambaran besar dari semua ini lebih mungkin sebuah pencapaian.
Ada banyak pemerintahan yang tidak melakukan apa-apa, katakan saja. Baiklah, lakukan dengan benar, saya sama. Atau ada orang yang tampaknya insinyur keren, tetapi secara umum di alam bawah sadar, mereka mengatakan ini: "Di Jawa, Anda perlu meningkatkan utas." Apa yang harus ditingkatkan? Apa itu utas?
Orang pada umumnya terlalu malas untuk berpikir.
Seperti dalam lelucon:
Petka dan Vasily Ivanovich terbang di pesawat.
Dengan mudah Ivanovich bertanya: - Petka, perangkat?
Balasan Petka: - 200!
Vasily Ivanovich: - Dan bagaimana dengan 200?
Petka: - Bagaimana dengan peralatan?
Saya akan menceritakan sebuah kisah. Saya berada di Ukraina musim semi ini, kami terbang dari Belarus (Anda mengerti mengapa itu tidak mungkin langsung dari St. Petersburg). Dan di bea cukai kami duduk selama sekitar dua jam, tidak kurang. Petugas bea cukai sangat baik, dalam semua keseriusannya bertanya apakah Jawa adalah teknologi yang usang. Orang-orang yang duduk di dekatnya yang terbang ke konf yang sama. Dan saya adalah tipe pembicara, saya harus menyodok, berdiri dan, seperti yang diharapkan, tanpa malu-malu berbicara tentang hal-hal yang tidak saya gunakan sama sekali. Dan sepanjang jalan dia berbicara tentang distribusi JDK yang disebut Liberica, ini adalah JDK untuk Raspberry Pi.
Dan bagaimana menurutmu. Bahkan enam bulan berlalu sebelum orang-orang mengetuk gerobak saya dan mengatakan bahwa, lihat, kami telah meletakkan solusi di Liber pada prod, dan saya sudah memiliki laporan tentang ini di jfuture.by konf Belarusia. Inilah pendekatannya. Ini bukan penginjil yang buruk, tetapi seorang pria, seorang insinyur normal.
Ngomong-ngomong, kita akan segera mengadakan konferensi Joker 2018 , yang akan mencakup Andrei Breslav (jelas-jelas mengobrak-abrik coroutine), dan Pasha Finkelstein , dan Josh Long dapat ditanya tentang dukungan Spring's Loom. Nah, dan sekelompok ahli terkemuka yang keren, ayolah!
Dan sekarang, kembali ke utas. Orang-orang mencoba menggambar pikiran melalui dua neuron mereka yang telah terdegradasi, mereka mengendus-endus dengan kepalan tangan mereka, dan bergumam: "Thread tidak begitu di Jawa, thread tidak begitu di Jawa." Perangkat! Peralatan apa? Ini umumnya neraka.
Dan inilah Presler, seorang lelaki yang normal, tidak terdegradasi, dan pada awalnya ia membuat deskripsi yang waras. Setahun kemudian, menggergaji demo yang berfungsi. Saya mengatakan semua ini sehingga Anda mengerti bahwa deskripsi masalah yang normal, dokumentasi normal adalah kepahlawanan dari jenis khusus. Dan demo umumnya ruang. Ini adalah orang pertama yang benar-benar melakukan sesuatu ke arah ini. Dia sangat membutuhkan.
Bersama dengan demo, Presler berbicara di konferensi dan merilis video ini:
Bahkan, seluruh artikel ini adalah ulasan tentang apa yang dikatakan di sana. Saya tidak berpura-pura sama sekali atas keunikan materi ini, semua yang ada di artikel ini ditemukan oleh Ron.
Diskusi ini tentang tiga topik yang menyakitkan:
- Kontinasi
- Serat
- Buntut panggilan
Mungkin, dia sangat muak dengan melihat Quasar dan bertarung dengan gangguannya sehingga tidak ada kekuatan - kamu harus mendorongnya menjadi runtime.
Itu setahun yang lalu, dan sejak itu mereka menggergaji sebuah prototipe. Beberapa sudah kehilangan harapan bahwa suatu hari nanti kita akan melihat demo, tetapi sebulan yang lalu mereka melahirkannya dan menunjukkan apa yang terlihat di tweet ini .

Ketiga topik yang menyakitkan dalam demo ini baik secara langsung dalam kode, atau setidaknya secara moral hadir. Ya, mereka belum menguasai panggilan ekor, tapi mereka mau.
Masalah
Pengguna yang tidak bahagia, pengembang aplikasi, ketika mereka membuat API, dipaksa untuk memilih di antara dua kursi. Puncak dibangun di atas satu kursi, bunga tumbuh di kursi lainnya. Dan tidak ada yang cocok dengan kita.

Misalnya, jika Anda menulis layanan yang bekerja secara serempak, itu berfungsi dengan baik dengan kode lawas, mudah untuk debug dan memantau kinerja. Masalah akan muncul dengan bandwidth dan skalabilitas. Hanya karena jumlah utas yang sekarang dapat Anda jalankan di perangkat sederhana, pada perangkat keras komoditas - yah, katakanlah, dua ribu. Ini jauh lebih sedikit daripada jumlah koneksi yang bisa dibuka ke server ini. Yang dari sudut pandang netcode bisa hampir tak ada habisnya.
(Ya, ya, ini ada hubungannya dengan fakta bahwa soket di Jawa diatur secara moral, tetapi ini adalah topik untuk percakapan lain)
Bayangkan Anda sedang menulis semacam MMO.

Sebagai contoh, selama Perang Utara di EVE Online, dua ribu empat ratus pilot berkumpul pada satu titik di angkasa, masing-masing - secara kondisional, apakah itu ditulis di Jawa - bukan hanya satu utas, tetapi beberapa. Dan pilot, tentu saja, adalah logika bisnis yang kompleks, dan bukan penerbitan HTML apa pun yang dapat dikesampingkan dengan tangan di kaca pembesar.
Waktu respons dalam pertempuran itu begitu lama sehingga pemain harus menunggu beberapa menit untuk menunggu tembakan. Sejauh yang saya tahu, PKC khusus untuk pertempuran itu melemparkan sumber daya perangkat keras yang sangat besar dari klusternya.
Meskipun, saya mungkin mengutip EVE sebagai contoh dengan sia-sia, karena, sejauh yang saya mengerti, semuanya ditulis dalam Python, dan dalam Python dengan multithreading masih lebih buruk daripada kita - dan kita dapat mempertimbangkan persaingan fitur fitur bahasa yang buruk. Tapi kemudian contohnya jelas dan dengan gambar.
Jika Anda tertarik pada subjek IMO secara umum dan sejarah "Perang Utara" khususnya, baru-baru ini sebuah video yang sangat bagus tentang topik ini telah muncul di saluran Bulzhat (apa pun nama itu berarti), tonton dari stempel waktu saya.
Kami kembali ke topik.
Di sisi lain, Anda dapat menggunakan semacam kerangka asinkron. Itu scalable. Tetapi kami akan segera jatuh ke dalam debugging yang sangat sulit, profiling kinerja yang rumit, kami tidak akan dapat mengintegrasikannya secara mulus dengan warisan, Anda harus menulis ulang banyak hal, membungkusnya dengan pembungkus yang buruk, dan umumnya merasa seperti kami baru saja diperkosa. Beberapa kali berturut-turut. Selama berhari-hari, pada kenyataannya, sepanjang waktu kami menulis ini, Anda harus merasa seperti itu.
Saya bertanya kepada pakar, akademisi terkenal Escobar, apa pendapatnya tentang ini:

Apa yang harus dilakukan Yang disebut faybers bergegas untuk menyelamatkan.
Dalam kasus umum, serat adalah benang yang sangat ringan yang juga menggeledah ruang alamat (karena keajaiban tidak terjadi, Anda mengerti). Tetapi tidak seperti utas biasa, mereka tidak menggunakan multitasking preemptive, tetapi multitasking kooperatif. Baca lebih lanjut di Wikipedia .
Fiber dapat mewujudkan keunggulan pemrograman sinkron dan asinkron. Akibatnya, pemanfaatan besi meningkat, dan kami menggunakan lebih sedikit server di gugus untuk tugas yang sama. Nah, di saku kita untuk ini kita mendapatkan lavender. Babos Lave. Uang Nah, Anda mengerti intinya. Untuk server yang disimpan.
Di persimpangan jalan
Hal pertama yang ingin saya diskusikan. Orang tidak mengerti perbedaan antara kelanjutan dan serat.
Sekarang akan ada pencerahan Kultus!
Kami akan mengumumkan fakta: Kelanjutan dan Serat adalah dua hal yang berbeda.
Lanjutan
Serat dibangun di atas mekanik yang disebut Lanjutan.
Lanjutan (lebih tepatnya, lanjutan terbatas) adalah semacam perhitungan, eksekusi, bagian dari program yang dapat tertidur, lalu bangun dan melanjutkan eksekusi dari tempat ia tertidur. Kadang-kadang bahkan bisa dikloning atau serial, bahkan ketika dia sedang tidur.
Saya akan menggunakan kata "kelanjutan", dan bukan "kelanjutan" (seperti yang tertulis di Wikipedia), karena kita semua berkomunikasi dalam bahasa Inggris . Dengan menggunakan terminologi Rusia yang normal, seseorang dapat dengan mudah sampai pada situasi di mana perbedaan antara istilah Rusia dan Inggris menjadi terlalu besar dan tidak ada orang lain yang mengerti arti dari apa yang dikatakan.
Kadang-kadang saya juga akan menggunakan kata "crowding out" alih-alih versi bahasa Inggris "yield". Hanya kata "hasil" - ini semacam benar-benar jahat. Karena itu akan ada “crowding out”.
Jadi disini. Sangat penting bahwa tidak boleh ada persaingan dalam kontinum. Itu sendiri adalah primitif minimal dari proses ini.
Anda dapat menganggap kelanjutan sebagai Runnable
, di dalamnya Anda dapat memanggil metode pause()
. Itu ada di dalam dan langsung, karena multitasking kami kooperatif. Dan kemudian Anda dapat menjalankannya lagi, dan alih-alih menghitung ulang semuanya, ia akan melanjutkan dari tempat ia tinggalkan. Sihir semacam itu. Kami akan kembali ke sihir.
Di mana mendapatkan demo dengan kelanjutan kerja - kita akan membahas di bagian paling akhir. Sekarang mari kita bicara tentang apa yang ada di sana.
Kelas kelanjutan itu sendiri terletak di java.base, semua tautan akan ada dalam deskripsi. ( src/java.base/share/classes/java/lang/Continuation.java
). Tetapi kelas ini sangat besar, banyak, sehingga masuk akal untuk melihat hanya semacam tekanan dari itu.
public class Continuation implements Runnable { public Continuation(ContinuationScope scope, Runnable body); public final void run(); public static void yield(ContinuationScope scope); public boolean isDone(); protected void onPinned(Reason reason) { throw new IllegalStateException("Pinned: " + reason); } }
Perhatikan bahwa sebenarnya file ini terus berubah. Misalnya, pada hari sebelumnya, kelanjutan tidak mengimplementasikan antarmuka Runnable
. Perlakukan ini sebagai semacam sketsa.
Lihatlah konstruktornya. body
- ini adalah kode yang Anda coba jalankan, dan scope
- adalah semacam lompatan yang memungkinkan Anda untuk membuat sarang dalam kelanjutan.
Karenanya, Anda dapat menjadwalkan kode ini hingga akhir dengan run
metode, atau menggantikannya dengan beberapa larik tertentu menggunakan metode yield
(larik diperlukan di sini untuk sesuatu seperti meneruskan tindakan ke penangan yang bersarang, tetapi kami tidak peduli sebagai pengguna). Anda dapat bertanya menggunakan metode isDone
jika semuanya selesai sampai akhir.
Dan untuk alasan yang ditentukan semata-mata oleh kebutuhan implementasi saat ini (tetapi kemungkinan besar, itu juga akan dimasukkan dalam rilis), tidak selalu memungkinkan untuk yield
. Misalnya, jika di dalam kelanjutan kami memiliki transisi ke kode asli dan bingkai asli muncul di tumpukan, maka tidak mungkin terjebak. Ini juga akan terjadi jika Anda mencoba untuk diperas sementara monitor asli, seperti metode yang disinkronkan, diambil di dalam tubuh kontinum. Secara default, ketika Anda mencoba memalsukan ini, pengecualian dilemparkan ... tetapi serat yang dibangun di atas kelanjutan membebani metode ini dan melakukan sesuatu yang lain. Ini akan sedikit nanti.
Anda dapat menggunakan ini kira-kira dengan cara berikut:
Continuation cont = new Continuation(SCOPE, () -> { while (true) { System.out.println("before"); Continuation.yield(SCOPE); System.out.println("after"); } }); while (!cont.isDone()) { cont.run(); }
Ini adalah contoh dari presentasi Presler. Sekali lagi, ini bukan kode "sepele", ini semacam sketsa.
Ini adalah sketsa dari apa yang kita lakukan kelanjutan, di tengah kelanjutan ini kita ramai keluar dan kemudian dalam siklus tanpa akhir kita bertanya apakah kelanjutan bekerja sampai akhir dan apakah itu harus dilanjutkan.
Tetapi secara umum, itu tidak dimaksudkan bahwa pemrogram aplikasi biasa akan berhubungan dengan API ini. Ini dimaksudkan untuk pencipta kerangka kerja sistem. Kerangka kerja pembentuk sistem seperti Spring Framework akan segera mengadopsi fitur ini segera setelah keluar. Kamu akan lihat. Anggap ini prediksi. Prediksi yang ringan, karena semuanya cukup jelas di sini. Semua data untuk prediksi adalah. Fitur ini terlalu penting untuk tidak diadaptasi. Karena itu, tidak perlu khawatir sebelumnya bahwa seseorang akan menyiksa Anda dengan penyandian dalam formulir ini. Nah, jika Anda adalah pengembang Spring, maka Anda tahu apa yang Anda lakukan.
Dan sekarang, di atas kelanjutan, serat dibangun.
Serat
Jadi, apa yang ada dalam kasus kami artinya serat.
Ini adalah semacam abstraksi, yaitu:
- Thread ringan diproses dalam JVM itu sendiri, dan bukan di sistem operasi;
- Dengan biaya overhead yang sangat rendah untuk membuat, mempertahankan hidup, berpindah tugas;
- Yang bisa dijalankan jutaan kali.
Banyak teknologi mencoba membuat serat dengan satu atau lain cara. Sebagai contoh, di Kotlin ada coroutine yang diimplementasikan pada generasi bytecode yang sangat cerdas. SANGAT CERDAS . Tetapi runtime adalah tempat yang lebih baik untuk mengimplementasikan hal-hal seperti itu.
Minimal, JVM sudah tahu cara menangani utas dengan baik, dan yang perlu kita lakukan adalah merampingkan proses pengkodean untuk multithreading. Anda dapat menggunakan API asinkron, tetapi ini hampir tidak bisa disebut "penyederhanaan": bahkan menggunakan hal-hal seperti Reaktor , Reaktor Proyek Musim Semi, yang memungkinkan penulisan kode yang tampaknya linier, tidak akan banyak membantu jika Anda perlu men-debug masalah yang rumit.
Jadi Fiber.
Serat terdiri dari dua komponen. Ini adalah:
Itu adalah:

Anda dapat memutuskan siapa perencana di sini. Saya pikir perencana di sini adalah Jay.
- Fibre membungkus kode yang ingin Anda jalankan dalam kelanjutan
- Penjadwal meluncurkannya di kumpulan utas operator
Saya akan memanggil mereka utas pembawa.

Prototipe saat ini menggunakan java.util.concurrent.Executor
, dan penjadwal ForkJoinPool
. Kami memiliki segalanya. Di masa depan, sesuatu yang lebih pintar mungkin muncul di sana, tetapi untuk saat ini, seperti ini.
Bagaimana kelanjutannya:
- Itu ramai keluar (menghasilkan) ketika kunci terjadi (misalnya, pada IO);
- Berlanjut saat siap untuk melanjutkan (misalnya, operasi IO telah selesai dan Anda dapat melanjutkan).
Status pekerjaan saat ini:
- Fokus utama pada filosofi, konsep;
- API tidak diperbaiki, itu "untuk pertunjukan". Ini adalah prototipe penelitian;
- Ada prototipe kerja enkode yang sudah jadi dari kelas
java.lang.Fiber
.
Itu akan dibahas.
Apa yang sudah digergaji menjadi serat:
- Ini menjalankan peluncuran tugas;
- Parkir mobil tidak diparkir di atas kapal induk;
- Menunggu selesainya serat.
Diagram sirkuit
mount(); try { cont.run(); } finally () { unmount(); }
- Kita bisa memasang serat pada pembawa benang;
- Kemudian jalankan kelanjutannya;
- Dan tunggu sampai dia penuh sesak atau dengan jujur berhenti;
- Pada akhirnya, kami selalu meninggalkan utas.
Pseudo-code ini akan dieksekusi pada ForkJoinPool
atau yang lainnya (yang nantinya akan menjadi versi final).
Gunakan dalam kenyataan
Fiber f = Fiber.execute( () -> { System.out.println("Good Morning!"); readLock.lock(); try { System.out.println("Good Afternoon"); } finally { readLock.unlock(); } System.out.println("Good Night"); });
Lihat, kami membuat serat di mana:
- selamat datang semuanya;
- kami memblokir di reentrant loke;
- sekembalinya, selamat atas makan siang Anda;
- akhirnya lepaskan kuncinya;
- dan ucapkan selamat tinggal.
Semuanya sangat sederhana.
Kami tidak menyebabkan crowding secara langsung. Project Loom sendiri tahu bahwa ketika readLock.lock();
dipicu readLock.lock();
dia harus turun tangan dan secara implisit melakukan represi. Pengguna tidak melihat ini, tetapi itu terjadi di sana.
Tumpukan, tumpukan di mana-mana!
Mari kita tunjukkan apa yang terjadi menggunakan tumpukan pizza sebagai contoh.
Pada awalnya, utas pembawa dalam keadaan menunggu, dan tidak ada yang terjadi.

Atas tumpukan di atas, ingat.
Kemudian serat dijadwalkan untuk dieksekusi, dan tugas serat mulai berjalan.

Di dalam dirinya, ia jelas meluncurkan kelanjutan, di mana kode sebenarnya sudah ada.

Dari sudut pandang pengguna, kami belum meluncurkan apa pun di sini.
Itu hanya bingkai pertama dari kode pengguna muncul di tumpukan, dan itu ditandai dengan warna ungu.
Selanjutnya, kode dieksekusi, dieksekusi, di beberapa titik tugas mencoba menangkap kunci dan memblokirnya, yang mengarah ke crowding out otomatis.

Segala sesuatu yang ada di tumpukan kelanjutan disimpan di tempat magis tertentu. Dan menghilang.

Seperti yang Anda lihat, aliran kembali ke serat, ke instruksi yang mengikuti Continuation.run
. Dan ini adalah akhir dari kode serat.
Tugas serat berakhir, pembawa media sedang menunggu pekerjaan baru.

Serat diparkir, di suatu tempat terletak, kontinum benar-benar ramai.
Cepat atau lambat, saatnya tiba ketika orang yang memiliki kunci melepaskannya.
Ini mengarah pada fakta bahwa serat, yang sedang menunggu pelepasan kunci, dibongkar. Tugas serat ini dimulai lagi.
- Reentrantlock.unlock
- Locksupport.unpark
- Fiber.unpark
- ForkJoinPool.execute
Dan kami dengan cepat kembali ke tumpukan, yang baru-baru ini.

Selain itu, utas pengangkut mungkin sangat berbeda. Dan itu masuk akal!
Jalankan kelanjutan lagi.

Dan inilah MAGIC !!! Stack dikembalikan, dan eksekusi berlanjut dengan instruksi setelah Continuation.yield
.

Kami merangkak keluar dari kunci yang baru saja diparkir dan mulai mengeksekusi semua kode yang tersisa dalam kelanjutan:

Tugas pengguna berakhir, dan kontrol kembali ke tugas serat segera setelah instruksi continue.run

Pada saat yang sama, eksekusi serat berakhir, dan kami kembali menemukan diri kami dalam mode siaga.

Peluncuran serat berikutnya lagi memulai seluruh siklus kelahiran kembali yang dijelaskan di atas.

Contoh langsung
Dan siapa yang pernah mengatakan bahwa semua ini berhasil? Apakah ini tentang sepasang microbenchmark yang ditulis pada malam hari?
Sebagai contoh operasi petasan, Oraklovites menulis server web kecil dan memberi makan dengan permintaan sehingga tersedak. Kemudian mereka dipindahkan ke serat. Server berhenti tersedak, dan dari sini kami menyimpulkan bahwa serat bekerja.
Saya tidak memiliki kode persis untuk server ini, tetapi jika posting ini mendapat cukup suka dan komentar, saya akan mencoba menulis contoh sendiri dan membuat grafik nyata.
Masalahnya
Apakah ada masalah di sini? Ya tentu saja! Seluruh cerita dengan para penipu adalah kisah tentang masalah dan pengorbanan yang berkelanjutan.
Masalah filosofis
- Apakah kita perlu menemukan kembali utas?
- Haruskah semua kode yang ada berfungsi dengan baik di dalam fiber?
Prototipe saat ini berjalan dengan keterbatasan. Yang mungkin masuk ke rilis, meskipun saya tidak mau. Namun, OpenJDK adalah hal yang menghargai kompatibilitas tanpa akhir.
Apa batasan teknisnya? Batasan yang paling jelas adalah 2 buah.
Masalahnya sekali - Anda tidak dapat mengganti bingkai asli
PrivilegedAction<Void> pa = () -> { readLock.lock();
Di sini doPrivileged memanggil metode asli.
Anda memanggil doPrivileged
, melompat keluar dari VM, bingkai asli muncul di tumpukan Anda, setelah itu Anda mencoba memarkir di baris readLock.lock()
. Dan pada saat itu, utas pembawa akan ternoda sampai tidak dijiplak. Artinya, utasnya hilang. Dalam hal ini, benang pembawa dapat berakhir, dan secara umum, ini mematahkan seluruh ide serat.
Cara untuk menyelesaikan ini sudah diketahui, dan diskusi sedang berlangsung tentang hal ini.
Masalah dua - blok disinkronkan
Ini adalah sampah yang jauh lebih serius
synchronized (object) {
synchronized (object) {
Dalam hal monitor menangkap serat, benang pembawa juga menendang.
Jelas bahwa dalam kode yang sama sekali baru Anda dapat mengubah monitor menjadi kunci langsung, alih-alih menunggu + memberi tahu Anda dapat menggunakan objek kondisi, tetapi apa yang harus dilakukan dengan warisan? Ini masalah.
API utas? Thread.currentThread ()? Penduduk setempat?
Dalam prototipe saat ini, Thread
and Fiber
telah membuat satu superclass umum yang disebut Strand
.
Ini memungkinkan Anda untuk mentransfer API dengan cara yang paling minimal.
Apa yang harus dilakukan selanjutnya - seperti biasa dalam proyek ini, adalah sebuah pertanyaan.
Apa yang terjadi dengan Thread API sekarang?
- Penggunaan pertama
Thread.currentThread()
dalam sebuah serat menciptakan semacam bayangan benang, Bayangan Thread; - dari sudut pandang sistem, ini adalah utas yang "belum dirilis", dan tidak ada informasi meta VM di dalamnya;
- ST mencoba untuk meniru semua yang ia bisa;
- tetapi Anda harus memahami bahwa API lama memiliki banyak sampah;
- lebih khusus, Shadow Thread mengimplementasikan Thread Thread untuk semua hal kecuali
stop
, suspend
, resume
dan menangani pengecualian yang tidak tertangkap.
Apa yang harus dilakukan dengan utas lokal?
- sekarang utas penduduk setempat berubah menjadi serat lokal;
- ada banyak masalah dengan ini, semua ini sedang dibahas;
- seperangkat kegunaan terutama dibahas;
- Utas secara historis menggunakan keduanya dengan benar dan salah (mereka yang menggunakan salah masih berharap untuk sesuatu, dan Anda tidak dapat sepenuhnya mengecewakan mereka);
- secara umum, ini menciptakan berbagai macam aplikasi:
- Tingkat tinggi: cache koneksi atau kata sandi dalam wadah;
- Tingkat rendah: prosesor di perpustakaan sistem.
Berapa makan semuanya

Utas:
- Stack: 1MB dan 16KB pada struktur data kernel;
- Contoh per utas: 2300 byte, termasuk informasi meta VM.
Serat:
- Tumpukan lanjutan: dari ratusan byte ke kilobyte;
- Per fiber misalnya: 200-240 byte.
Perbedaannya sangat besar!
Dan itulah yang membuat jutaan orang bersemangat.
Apa yang bisa parkir
Jelas bahwa hal yang paling ajaib adalah parkir otomatis ketika beberapa peristiwa terjadi. Apa yang saat ini didukung?
- Thread.sleep, gabung;
- java.util.concurrent dan LockSupport.lock;
- IO: jaringan pada soket (soket baca, tulis, sambungkan, terima), file, pipa;
- Semua ini belum selesai, tetapi cahaya di terowongan terlihat.
Komunikasi antar Fiber
Pertanyaan lain yang diajukan semua orang adalah: bagaimana cara bertukar informasi di antara serat secara kompetitif.
- Prototipe saat ini meluncurkan tugas di
Runnable
, dapat dikonversi ke CompletableFuture
, jika karena alasan tertentu Anda perlu; - java.util.concurrent "just works." Anda dapat meraba-raba segala sesuatu dengan cara standar;
- mungkin ada API baru untuk multithreading, tetapi ini tidak akurat;
- banyak pertanyaan kecil seperti "haruskah serat mengembalikan nilai?"; semuanya dibahas, mereka tidak ada dalam prototipe.
Bagaimana kelanjutan diimplementasikan dalam prototipe?
Persyaratan yang jelas dibebankan pada kelanjutan: Anda perlu menggunakan RAM sesedikit mungkin, dan Anda harus beralih di antara mereka secepat mungkin. Kalau tidak, itu tidak akan berhasil untuk menjaga mereka dalam jutaan. Tugas utama di sini adalah entah bagaimana tidak melakukan salinan penuh tumpukan untuk setiap parkir-uppark. Dan ada skema seperti itu! Mari kita coba jelaskan ini di gambar.
Cara paling keren tentu saja dengan meletakkan semua tumpukan di pinggul java dan menggunakannya secara langsung. Tetapi tidak jelas bagaimana cara membuat kode sekarang, jadi prototipe menggunakan penyalinan. Tetapi menyalin dengan hack kecil tapi penting.
Kami punya dua kursi ... Maksudku, dua tumpukan. Dua array java di pinggul. Salah satunya adalah array objek, di mana kita akan menyimpan referensi ke objek. Yang kedua adalah primitif (misalnya, intim), yang akan menangani segalanya.

Sekarang kita berada dalam keadaan di mana kelanjutan akan dilakukan untuk pertama kalinya.
run
panggilan metode internal yang disebut enter
:

Dan kemudian kode pengguna dieksekusi, sampai crowding out pertama.

Pada titik ini, panggilan VM dibuat, yang panggilannya freeze
. Dalam prototipe ini, ini dilakukan secara fisik - menggunakan salinan.

Kami memulai proses menyalin frame secara berurutan dari tumpukan asli ke java hip.

Penting untuk memeriksa apakah monitor dipegang di sana atau kode asli digunakan, atau sesuatu yang benar-benar tidak memungkinkan kita untuk terus bekerja.

Dan jika semuanya baik-baik saja, kita salin dulu ke array primitif:

Kemudian kita mengisolasi referensi ke objek dan menyimpannya di array objek:

Sebenarnya, dua teh untuk semua orang yang membaca ke tempat ini!
Selanjutnya kami melanjutkan prosedur ini untuk semua elemen lain dari tumpukan asli.

Hore! Kami menyalin semuanya ke sarang di pinggul. Anda dapat dengan aman melompat ke tempat panggilan tanpa takut bahwa kami telah kehilangan sesuatu. Semuanya keren.

Sekarang, cepat atau lambat, kode panggilan akan memanggil kelanjutan kami lagi. Dan dia harus melanjutkan dari tempat di mana dia ditinggalkan terakhir kali. Ini tugas kita.

Memeriksa apakah kelanjutan berjalan, mengatakan ya, itu berjalan. Jadi, Anda perlu memanggil VM, membersihkan beberapa ruang di tumpukan dan memanggil fungsi VM internal. "Thaw" diterjemahkan ke dalam bahasa Rusia sebagai "thaw", "unfreeze", yang terdengar cukup logis. Kita perlu mencairkan frame dari tumpukan lanjutan ke tumpukan asli utama kita.
Saya tidak yakin teh yang mencairkan cukup jelas. Abstraksi yang buruk seperti anak kucing dengan pintu. Tetapi ini akan bermanfaat bagi kita.

Kami membuat salinan yang cukup jelas.
Pertama dengan array primitif:

Kemudian dari tautan:

Anda perlu menambal bit yang disalin untuk mendapatkan tumpukan yang benar:

Ulangi kecabulan untuk semua bingkai:

Sekarang Anda dapat kembali untuk yield
dan melanjutkan seolah-olah tidak ada yang terjadi.

Masalahnya adalah bahwa salinan penuh tumpukan itu sama sekali tidak seperti yang ingin kita miliki. Ini sangat menghambat. Semua ini adalah isolasi tautan, periksa pinning, tidak cepat. Dan yang paling penting - semua ini secara linear tergantung pada ukuran tumpukan! Singkatnya, neraka. Tidak perlu melakukan ini.
Sebaliknya, kami punya ide lain - menyalin malas.
Mari kita kembali ke tempat di mana kita sudah memiliki kelanjutan beku.

Kami melanjutkan proses seperti sebelumnya:

Dengan cara yang sama seperti sebelumnya, kami membersihkan tempat di tumpukan asli:

Tapi kami tidak menyalin semuanya dalam satu baris, tetapi hanya satu atau beberapa bingkai:

Sekarang retas. Anda perlu menambal alamat pengirim metode C
sehingga menunjuk ke penghalang pengembalian tertentu:

Sekarang Anda dapat dengan aman kembali ke yield
:

Yang pada gilirannya akan mengarah pada panggilan ke kode pengguna dalam metode C
:

Sekarang bayangkan C
ingin kembali ke kode yang memanggilnya. Tapi pemanggilnya adalah B
, dan dia tidak ada di tumpukan! Karena itu, ketika dia mencoba untuk kembali, dia akan pergi ke alamat pengirim, dan alamat ini sekarang menjadi penghalang kembali. Dan, Anda tahu, ini sekali lagi akan menarik panggilan thaw
:

Dan thaw
akan mencairkan frame berikutnya pada tumpukan kelanjutan, dan ini adalah B
:

Bahkan, kami menyalinnya dengan malas, berdasarkan permintaan.
Selanjutnya, kita menjatuhkan B
dari tumpukan kelanjutan dan mengatur penghalang lagi (penghalang perlu diatur karena ada sesuatu yang tersisa pada tumpukan kelanjutan). Dan seterusnya.

Tetapi anggaplah B
tidak akan kembali ke kode panggilan, tetapi pertama-tama memanggil beberapa metode lain D
Dan metode baru ini juga ingin dihalangi.

Dalam hal ini, ketika tiba saatnya untuk melakukan freeze
, kita hanya perlu menyalin bagian atas tumpukan asli ke tumpukan lanjutan:

Dengan demikian, jumlah pekerjaan yang dilakukan tidak tergantung secara linear pada ukuran tumpukan. Secara linear hanya tergantung pada jumlah frame yang sebenarnya kita gunakan dalam pekerjaan.
Apa yang tersisa
Pengembang mengingat beberapa fitur, tetapi mereka tidak masuk ke prototipe.
- Serialisasi dan kloning. Kemampuan untuk melanjutkan di komputer lain, di waktu lain, dll.
- JVM TI dan debugging, seolah-olah itu adalah utas biasa. Jika Anda diblokir saat membaca soket, maka Anda tidak akan melihat lompatan indah dari hasil, dalam prototipe utas hanya akan diblokir, seperti utas biasa lainnya.
- Rekursi ekor bahkan tidak tersentuh.
Langkah selanjutnya:
- Buat API manusia;
- Tambahkan semua fitur yang hilang;
- Tingkatkan kinerja.
Dari mana mendapatkan
Prototipe dibuat sebagai brunch di repositori OpenJDK. Anda dapat mengunduh prototipe di sini dengan beralih ke fibers
brunch.
Ini dilakukan seperti ini:
$ hg clone http://hg.openjdk.java.net/loom/loom $ cd loom $ hg update -r fibers $ sh configure $ make images
Seperti yang Anda tahu, semua ini akan memulai perakitan OpenJDK sialan seluruh. Karena itu, pertama, setengah jam berikutnya hidup Anda harus melakukan sesuatu yang lain, sementara semua ini akan terjadi.

Kedua, Anda harus memiliki komputer yang dikonfigurasi dengan benar dengan C ++ toolchain dan libs GNU. Saya mengisyaratkan bahwa Anda tidak disarankan melakukan ini di Windows. Serius, bahkan dengan mengunduh VirtualBox dan menginstal Ubuntu baru di sana, Anda akan menghabiskan pesanan dengan jumlah yang lebih sedikit daripada mencoba untuk menyadari kesalahan tidak manusiawi lainnya ketika membangun dari Cygwin atau msys64. Di sinilah msys datang bahkan lebih buruk daripada Cygwin.
Meskipun ini, tentu saja, semua bohong, saya hanya bosan menulis instruksi perakitan kepada Anda .
- , mercurial extension fsmonitor. , , hg help -e fsmonitor
.
~/.hgrc :
[fsmonitor] mode = on
- . -, cp -R ./loom ./loom-backup
.
, . , Java- , .
sh configure
- . , Ubuntu, Autoconf ( sudo apt-get install autoconf
). — OpenJDK Ubuntu, , . Windows , .
, , hg diff --stat -r default:fibers
.
, , , .
Kesimpulan
«, ». «», . «Loom» — « ». Project Loom .
, . , «» , , — , , , — .
, , XIX , .

. -, .
, . IDE .
, , , « », «», « » .
? . .
Terima kasih