Di perpustakaan di Java 7, saya memiliki kelas yang menyediakan layanan ke kelas lain. Setelah membuat turunan dari kelas layanan ini, satu metode dapat dipanggil beberapa kali (sebut saja doWork()
metode). Jadi saya tidak tahu kapan pekerjaan kelas layanan selesai.
Masalahnya adalah kelas layanan menggunakan benda berat dan harus melepaskannya. Saya mengatur bagian ini dalam suatu metode (sebut saja release()
), tetapi tidak dijamin bahwa pengembang lain akan menggunakan metode ini.
Apakah ada cara untuk memaksa pengembang lain untuk memanggil metode ini setelah menyelesaikan tugas kelas layanan? Tentu saja saya bisa mendokumentasikannya, tetapi saya ingin memaksa mereka.
Catatan: Saya tidak dapat memanggil release()
metode dalam doWork()
metode ini, karena doWork()
membutuhkan objek tersebut ketika dipanggil selanjutnya.
AutoCloseable
dibuat untuk tujuan yang tepat ini.Jawaban:
Solusi pragmatis adalah membuat kelas
AutoCloseable
, dan menyediakanfinalize()
metode sebagai backstop (jika perlu ... lihat di bawah!). Kemudian Anda bergantung pada pengguna kelas Anda untuk menggunakan try-with-resource atau meneleponclose()
secara eksplisit.Sayangnya, tidak ada cara 1 di Jawa untuk memaksa programmer untuk melakukan hal yang benar. Yang terbaik yang bisa Anda lakukan adalah mengambil penggunaan yang salah dalam penganalisa kode statis.
Pada topik finalizers. Fitur Java ini memiliki sangat sedikit kasus penggunaan yang baik . Jika Anda bergantung pada finalizer untuk merapikan, Anda mengalami masalah yang bisa memakan waktu sangat lama untuk merapikannya. Finalizer hanya akan dijalankan setelah GC memutuskan bahwa objek tidak lagi dapat dijangkau. Itu mungkin tidak terjadi sampai JVM melakukan pengumpulan penuh .
Jadi, jika masalah yang ingin Anda selesaikan adalah merebut kembali sumber daya yang perlu dirilis lebih awal , maka Anda telah ketinggalan perahu dengan menggunakan finalisasi.
Dan kalau-kalau Anda tidak mendapatkan apa yang saya katakan di atas ... hampir tidak pernah tepat untuk menggunakan finalizer dalam kode produksi, dan Anda tidak boleh mengandalkan mereka!
1 - Ada beberapa cara. Jika Anda siap untuk "menyembunyikan" objek layanan dari kode pengguna atau mengontrol siklus hidupnya dengan ketat (mis. Https://softwareengineering.stackexchange.com/a/345100/172 ) maka kode pengguna tidak perlu menelepon
release()
. Namun, API menjadi lebih rumit, dibatasi ... dan IMO "jelek". Juga, ini tidak memaksa programmer untuk melakukan hal yang benar . Ini menghilangkan kemampuan programmer untuk melakukan hal yang salah!sumber
DefaultResource
danFinalizableResource
yang memperpanjang yang pertama dan menambahkan finalizer. Dalam produksi Anda menggunakanDefaultResource
yang tidak memiliki finalizer-> tidak ada overhead. Selama pengembangan dan pengujian Anda mengonfigurasi aplikasi untuk menggunakanFinalizableResource
yang memiliki finalizer dan karenanya menambah biaya tambahan. Selama dev dan pengujian itu bukan masalah, tetapi itu bisa membantu Anda mengidentifikasi kebocoran dan memperbaikinya sebelum mencapai produksi. Namun jika kebocoran mencapai PRD, Anda dapat mengonfigurasi pabrik untuk membuat X%FinalizableResource
untuk membocorkannyaIni seperti use-case untuk
AutoCloseable
antarmuka dantry-with-resources
pernyataan yang diperkenalkan di Java 7. Jika Anda memiliki sesuatu sepertimaka konsumen Anda dapat menggunakannya sebagai
dan tidak perlu khawatir tentang memanggil eksplisit
release
terlepas dari apakah kode mereka melempar pengecualian.Jawaban tambahan
Jika yang benar-benar Anda cari adalah untuk memastikan tidak ada yang bisa lupa untuk menggunakan fungsi Anda, Anda harus melakukan beberapa hal
MyService
bersifat pribadi.MyService
yang memastikan itu dibersihkan setelah.Anda akan melakukan sesuatu seperti
Anda kemudian akan menggunakannya sebagai
Saya tidak merekomendasikan jalur ini pada awalnya karena itu mengerikan tanpa Java-8 lambdas dan antarmuka fungsional (di mana
MyServiceConsumer
menjadiConsumer<MyService>
) dan itu cukup mengesankan pada konsumen Anda. Tetapi jika yang Anda inginkan hanyalah yangrelease()
harus dipanggil, itu akan berhasil.sumber
try-with-resources
dan tidak hanya menghilangkantry
, sehinggaclose
panggilan tidak terjawab?try-with-resources
?.close()
dipanggil ketika kelas mengimplementasikanAutoCloseable
.using
blok untuk finalisasi deterministik? Setidaknya dalam C #, ini menjadi pola yang cukup jelas bagi pengembang untuk terbiasa menggunakan ketika memanfaatkan sumber daya yang membutuhkan finalisasi deterministik. Sesuatu yang mudah dan membentuk kebiasaan adalah hal terbaik berikutnya ketika Anda tidak bisa memaksa seseorang untuk melakukan sesuatu. stackoverflow.com/a/2016311/84206ya kamu bisa
Anda benar-benar dapat memaksa mereka untuk melepaskan, dan membuatnya jadi 100% mustahil untuk dilupakan, dengan membuatnya tidak mungkin bagi mereka untuk membuat
Service
instance baru .Jika mereka melakukan sesuatu seperti ini sebelumnya:
Kemudian ubah sehingga mereka harus mengaksesnya seperti ini.
Peringatan:
AutoCloseable
antarmuka yang disebutkan seseorang; tetapi contoh yang diberikan harus bekerja di luar kotak, dan kecuali untuk keanggunan (yang merupakan tujuan yang bagus dalam dirinya sendiri) tidak boleh ada alasan utama untuk mengubahnya. Perhatikan bahwa ini bermaksud untuk mengatakan bahwa Anda dapat menggunakanAutoCloseable
di dalam implementasi pribadi Anda; manfaat dari solusi saya atas penggunaan pengguna AndaAutoCloseable
adalah bahwa hal itu dapat, sekali lagi, tidak dilupakan.new Service
panggilan.Service
) berada di tangan si penelepon atau tidak berada di luar cakupan jawaban ini. Tetapi jika Anda memutuskan bahwa Anda benar-benar membutuhkan ini, ini adalah bagaimana Anda dapat melakukannya.sumber
Anda dapat mencoba memanfaatkan pola Command .
Ini memberi Anda kendali penuh atas akuisisi dan pelepasan sumber daya. Penelepon hanya mengirimkan tugas ke Layanan Anda, dan Anda sendiri yang bertanggung jawab untuk memperoleh dan membersihkan sumber daya.
Namun ada tangkapan. Penelepon masih dapat melakukan ini:
Tapi Anda bisa mengatasi masalah ini ketika sedang arisis. Ketika ada masalah kinerja dan Anda mengetahui bahwa beberapa penelepon melakukan ini, cukup tunjukkan cara yang tepat dan selesaikan masalah:
Anda dapat membuat kompleks sewenang-wenang ini dengan menerapkan janji-janji untuk tugas-tugas yang bergantung pada tugas-tugas lain, tetapi kemudian melepaskan barang juga menjadi lebih rumit. Ini hanya titik awal.
Async
Seperti yang telah ditunjukkan dalam komentar, hal di atas tidak benar-benar berfungsi dengan permintaan asinkron. Itu benar. Tetapi ini dapat dengan mudah diselesaikan di Java 8 dengan menggunakan CompleteableFuture , terutama
CompleteableFuture#supplyAsync
untuk menciptakan masa depan individu danCompleteableFuture#allOf
untuk merilis sumber daya setelah semua tugas selesai. Atau, seseorang selalu dapat menggunakan Utas atau Layanan Pelaksana untuk menggulung implementasi Futures / Janji mereka sendiri.sumber
Jika Anda bisa, jangan izinkan pengguna membuat sumber daya. Biarkan pengguna meneruskan objek pengunjung ke metode Anda, yang akan membuat sumber daya, meneruskannya ke metode objek pengunjung, dan lepaskan setelahnya.
sumber
AutoCloseable
melakukan hal yang sangat mirip di balik layar. Di bawah sudut lain, ini mirip dengan injeksi ketergantungan .Ide saya mirip dengan Polygnome.
Anda dapat memiliki metode "do_something ()" di kelas Anda hanya menambahkan perintah ke daftar perintah pribadi. Kemudian, miliki metode "commit ()" yang benar-benar berfungsi, dan panggil "release ()".
Jadi, jika pengguna tidak pernah memanggil commit (), pekerjaan itu tidak pernah dilakukan. Jika mereka melakukannya, sumber dayanya akan dibebaskan.
Anda kemudian dapat menggunakannya seperti foo.doSomething.doSomethineElse (). Commit ();
sumber
Alternatif lain dari yang disebutkan adalah penggunaan "referensi pintar" untuk mengelola sumber daya Anda yang telah dienkapsulasi dengan cara yang memungkinkan pengumpul sampah untuk secara otomatis membersihkan tanpa secara manual membebaskan sumber daya. Kegunaannya tentu saja tergantung pada implementasi Anda. Baca lebih lanjut tentang topik ini di posting blog yang luar biasa ini: https://community.oracle.com/blogs/enicholas/2006/05/04/understanding-weak-references
sumber
Untuk bahasa Berorientasi Kelas lainnya saya akan mengatakan memasukkannya ke dalam destructor, tetapi karena itu bukan pilihan saya pikir Anda dapat menimpa metode finalisasi. Dengan begitu ketika instance keluar dari ruang lingkup dan dikumpulkan sampah itu akan dirilis.
Saya tidak terlalu terbiasa dengan Java, tetapi saya pikir ini adalah tujuan finalisasi yang dimaksudkan.
http://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#finalize ()
sumber
If you rely on finalizers to tidy up, you run into the problem that it can take a very long time for the tidy-up to happen. The finalizer will only be run after the GC decides that the object is no longer reachable. That may not happen until the JVM does a full collection.