Bangun ObjectPool Anda sendiri di Java, Bagian 1

Ide penggabungan objek mirip dengan pengoperasian perpustakaan lokal Anda: Ketika Anda ingin membaca buku, Anda tahu bahwa lebih murah meminjam salinan dari perpustakaan daripada membeli salinan Anda sendiri. Demikian juga, lebih murah (dalam kaitannya dengan memori dan kecepatan) untuk suatu proses meminjam objek daripada membuat salinannya sendiri. Dengan kata lain, buku di perpustakaan merepresentasikan objek dan pelindung perpustakaan merepresentasikan proses. Ketika sebuah proses membutuhkan sebuah objek, ia memeriksa salinan dari kumpulan objek daripada membuat yang baru. Proses tersebut kemudian mengembalikan objek ke kumpulan saat tidak lagi diperlukan.

Namun, ada beberapa perbedaan kecil antara penggabungan objek dan analogi pustaka yang harus dipahami. Jika pelindung perpustakaan menginginkan buku tertentu, tetapi semua salinan dari buku itu sudah diperiksa, pelindung harus menunggu sampai salinannya dikembalikan. Kami tidak pernah ingin proses harus menunggu objek, jadi kumpulan objek akan membuat salinan baru jika diperlukan. Hal ini dapat menyebabkan sejumlah besar benda tergeletak di sekitar kolam, jadi ini juga akan menghitung benda yang tidak terpakai dan membersihkannya secara berkala.

Desain kumpulan objek saya cukup umum untuk menangani penyimpanan, pelacakan, dan waktu kedaluwarsa, tetapi instantiasi, validasi, dan penghancuran jenis objek tertentu harus ditangani dengan subclassing.

Sekarang dasar-dasarnya sudah keluar, mari kita lompat ke kode. Ini adalah objek kerangka:

 public abstract class ObjectPool { private long expirationTime; private Hashtable locked, unlocked; abstract Object create(); abstract boolean validate( Object o ); abstract void expire( Object o ); synchronized Object checkOut(){...} synchronized void checkIn( Object o ){...} } 

Penyimpanan internal dari objek yang dikumpulkan akan ditangani dengan dua Hashtableobjek, satu untuk objek terkunci dan yang lainnya untuk tidak terkunci. Objek itu sendiri akan menjadi kunci hashtable dan waktu penggunaan terakhirnya (dalam epoch milidetik) akan menjadi nilainya. Dengan menyimpan terakhir kali suatu objek digunakan, kumpulan dapat mengakhirinya dan mengosongkan memori setelah durasi tidak aktif yang ditentukan.

Pada akhirnya, kumpulan objek akan memungkinkan subclass untuk menentukan ukuran awal hashtable bersama dengan tingkat pertumbuhan dan waktu kedaluwarsa, tetapi saya mencoba membuatnya tetap sederhana untuk keperluan artikel ini dengan melakukan hard-coding nilai-nilai ini di konstruktor.

 ObjectPool() { expirationTime = 30000; // 30 seconds locked = new Hashtable(); unlocked = new Hashtable(); } 

The checkOut()cek pertama metode untuk melihat apakah ada objek di terkunci hashtable. Jika demikian, ia akan memutarnya dan mencari yang valid. Validasi bergantung pada dua hal. Pertama, kumpulan objek memeriksa untuk melihat bahwa waktu penggunaan terakhir objek tidak melebihi waktu kedaluwarsa yang ditentukan oleh subkelas. Kedua, kumpulan objek memanggil validate()metode abstrak , yang melakukan pemeriksaan khusus kelas atau inisialisasi ulang yang diperlukan untuk menggunakan kembali objek. Jika objek gagal validasi, itu dibebaskan dan loop berlanjut ke objek berikutnya dalam hashtable. Ketika sebuah objek ditemukan yang lolos validasi, itu dipindahkan ke hashtable yang terkunci dan dikembalikan ke proses yang memintanya. Jika hashtable yang tidak dikunci kosong, atau tidak ada objeknya yang lolos validasi, objek baru dibuat instance-nya dan dikembalikan.

 synchronized Object checkOut() { long now = System.currentTimeMillis(); Object o; if( unlocked.size() > 0 ) { Enumeration e = unlocked.keys(); while( e.hasMoreElements() ) { o = e.nextElement(); if( ( now - ( ( Long ) unlocked.get( o ) ).longValue() ) > expirationTime ) { // object has expired unlocked.remove( o ); expire( o ); o = null; } else { if( validate( o ) ) { unlocked.remove( o ); locked.put( o, new Long( now ) ); return( o ); } else { // object failed validation unlocked.remove( o ); expire( o ); o = null; } } } } // no objects available, create a new one o = create(); locked.put( o, new Long( now ) ); return( o ); } 

Itu adalah metode paling kompleks di ObjectPoolkelas, semuanya menurun dari sini. The checkIn()Metode hanya bergerak melewati-in objek dari hashtable terkunci ke dalam terkunci hashtable.

synchronized void checkIn( Object o ) { locked.remove( o ); unlocked.put( o, new Long( System.currentTimeMillis() ) ); } 

Tiga metode yang tersisa adalah abstrak dan oleh karena itu harus diimplementasikan oleh subclass. Demi artikel ini, saya akan membuat kumpulan koneksi database yang disebut JDBCConnectionPool. Inilah kerangkanya:

 public class JDBCConnectionPool extends ObjectPool { private String dsn, usr, pwd; public JDBCConnectionPool(){...} create(){...} validate(){...} expire(){...} public Connection borrowConnection(){...} public void returnConnection(){...} } 

Ini JDBCConnectionPoolakan membutuhkan aplikasi untuk menentukan driver database, DSN, nama pengguna, dan kata sandi pada saat pembuatan instance (melalui konstruktor). (Jika ini semua bahasa Yunani bagi Anda, jangan khawatir, JDBC adalah topik lain. Bertahanlah dengan saya sampai kita kembali ke penyatuan.)

 public JDBCConnectionPool( String driver, String dsn, String usr, String pwd ) { try { Class.forName( driver ).newInstance(); } catch( Exception e ) { e.printStackTrace(); } this.dsn = dsn; this.usr = usr; this.pwd = pwd; } 

Sekarang kita bisa menyelami implementasi metode abstrak. Seperti yang Anda lihat di checkOut()metode ini, ObjectPoolakan memanggil create () dari subkelasnya saat perlu membuat instance objek baru. Karena JDBCConnectionPool, yang harus kita lakukan adalah membuat Connectionobjek baru dan mengirimkannya kembali. Sekali lagi, demi menjaga artikel ini tetap sederhana, saya berhati-hati dan mengabaikan pengecualian dan kondisi null-pointer.

 Object create() { try { return( DriverManager.getConnection( dsn, usr, pwd ) ); } catch( SQLException e ) { e.printStackTrace(); return( null ); } } 

Sebelum ObjectPoolmembebaskan objek yang kedaluwarsa (atau tidak valid) untuk pengumpulan sampah, ia meneruskannya ke expire()metode subkelasnya untuk pembersihan menit terakhir yang diperlukan (sangat mirip dengan finalize()metode yang dipanggil oleh pengumpul sampah). Dalam kasus ini JDBCConnectionPool, yang perlu kita lakukan hanyalah menutup koneksi.

void expire( Object o ) { try { ( ( Connection ) o ).close(); } catch( SQLException e ) { e.printStackTrace(); } } 

Dan terakhir, kita perlu mengimplementasikan metode validate () yang ObjectPoolmemanggil untuk memastikan objek masih valid untuk digunakan. Ini juga tempat di mana inisialisasi ulang harus dilakukan. Sebab JDBCConnectionPool, kita tinggal mengecek apakah koneksinya masih terbuka.

 boolean validate( Object o ) { try { return( ! ( ( Connection ) o ).isClosed() ); } catch( SQLException e ) { e.printStackTrace(); return( false ); } } 

Itu saja untuk fungsionalitas internal. JDBCConnectionPoolakan memungkinkan aplikasi untuk meminjam dan mengembalikan koneksi database melalui metode yang sangat sederhana dan dinamai dengan tepat ini.

 public Connection borrowConnection() { return( ( Connection ) super.checkOut() ); } public void returnConnection( Connection c ) { super.checkIn( c ); } 

Desain ini memiliki beberapa kekurangan. Mungkin yang terbesar adalah kemungkinan membuat kumpulan besar objek yang tidak pernah dirilis. Misalnya, jika sekumpulan proses meminta objek dari kumpulan secara bersamaan, kumpulan akan membuat semua instance yang diperlukan. Kemudian, jika semua proses mengembalikan objek ke kolam, tetapi checkOut()tidak pernah dipanggil lagi, tidak ada objek yang dibersihkan. Ini jarang terjadi untuk aplikasi aktif, tetapi beberapa proses back-end yang memiliki waktu "idle" mungkin menghasilkan skenario ini. Saya memecahkan masalah desain ini dengan utas "pembersihan", tetapi saya akan menyimpan diskusi itu untuk paruh kedua artikel ini. Saya juga akan membahas penanganan error dan penyebaran pengecualian yang tepat untuk membuat kumpulan lebih kuat untuk aplikasi yang sangat penting.

Thomas E. Davis adalah Programmer Java Bersertifikat Sun. Dia saat ini tinggal di Florida Selatan yang cerah, tetapi menderita sebagai pecandu kerja dan menghabiskan sebagian besar waktunya di dalam ruangan.

Artikel ini, "Bangun ObjectPool Anda sendiri di Java, Bagian 1" awalnya diterbitkan oleh JavaWorld.