Java NIO (Input/Output Baru) - paket API input/output baru - diperkenalkan di J2SE 1.4 pada tahun 2002. Tujuan Java NIO adalah untuk meningkatkan kinerja tugas-tugas intensif I/O pada platform Java. Sepuluh tahun kemudian, banyak pengembang Java yang masih belum mengetahui cara memanfaatkan NIO sepenuhnya, dan bahkan lebih sedikit orang yang mengetahui bahwa API input/output yang diperbarui (NIO.2) diperkenalkan di Java SE 7. Kontribusi terbesar NIO dan NIO.2 pada platform Java adalah meningkatkan kinerja komponen inti dalam pengembangan aplikasi Java: pemrosesan input/output. Namun, tidak ada paket yang berguna dan tidak cocok untuk semua skenario. Jika digunakan dengan benar, Java NIO dan NIO.2 dapat sangat mengurangi waktu yang dihabiskan pada beberapa operasi I/O umum. Inilah kekuatan super NIO dan NIO.2, dan dalam artikel ini saya akan menunjukkan kepada Anda 5 cara sederhana untuk menggunakannya.
Ubah notifikasi (karena setiap acara memerlukan pendengar)
Selektor dan IO asinkron: Meningkatkan multiplexing melalui penyeleksi
Saluran - Janji dan Kenyataan
Pemetaan memori - baja yang bagus digunakan pada bilahnya
Pengkodean dan pencarian karakter
latar belakang NIO
Mengapa paket peningkatan yang telah ada selama 10 tahun merupakan paket I/O baru untuk Java? Alasannya adalah bagi sebagian besar pemrogram Java, operasi I/O dasar sudah cukup. Dalam pekerjaan sehari-hari, sebagian besar pengembang Java tidak perlu mempelajari NIO. Selangkah lebih maju, NIO lebih dari sekedar paket peningkatan kinerja. Sebaliknya, ini adalah kumpulan fungsi berbeda yang terkait dengan Java I/O. NIO mencapai peningkatan kinerja dengan membuat kinerja aplikasi Java "mendekati esensi", yang berarti bahwa API NIO dan NIO.2 membuka pintu masuk ke operasi sistem tingkat rendah. Harga dari NIO adalah meskipun memberikan kemampuan kontrol I/O yang lebih kuat, NIO juga mengharuskan kita untuk menggunakan dan berlatih lebih hati-hati dibandingkan pemrograman I/O dasar. Fitur lain dari NIO adalah fokusnya pada ekspresi aplikasi, yang akan kita lihat pada latihan berikut.
Mulai belajar NIO dan NIO.2
Ada banyak bahan referensi untuk NIO - beberapa link pilihan di bahan referensi. Untuk mempelajari NIO dan NIO.2, dokumentasi Java 2 SDK Standard Edition (SE) dan dokumentasi Java SE 7 sangat diperlukan. Untuk menggunakan kode dalam artikel ini, Anda perlu menggunakan JDK 7 atau lebih tinggi.
Bagi banyak pengembang, pertama kali mereka menemukan NIO mungkin saat memelihara aplikasi: aplikasi yang berfungsi menjadi semakin lambat, sehingga beberapa orang menyarankan penggunaan NIO untuk meningkatkan kecepatan respons. NIO unggul dalam hal meningkatkan kinerja aplikasi, namun hasil spesifiknya bergantung pada sistem yang mendasarinya (Perhatikan bahwa NIO bergantung pada platform). Jika Anda baru pertama kali menggunakan NIO, Anda perlu menimbangnya dengan cermat. Anda akan menemukan bahwa kemampuan NIO untuk meningkatkan kinerja tidak hanya bergantung pada OS, tetapi juga pada JVM yang Anda gunakan, konteks virtual host, karakteristik penyimpanan massal, dan bahkan data. Oleh karena itu, pekerjaan pengukuran kinerja relatif sulit dilakukan. Terutama ketika sistem Anda memiliki lingkungan penerapan portabel, Anda perlu memberikan perhatian khusus.
Setelah memahami konten di atas, kita tidak perlu khawatir. Sekarang mari kita rasakan lima fungsi penting NIO dan NIO.2.
1. Ubah notifikasi (karena setiap event membutuhkan pendengar)
Kekhawatiran umum di kalangan pengembang yang tertarik dengan NIO dan NIO.2 adalah kinerja aplikasi Java. Menurut pengalaman saya, pemberitahuan perubahan file di NIO.2 adalah fitur yang paling menarik (dan diremehkan) dari API input/output baru.
Banyak aplikasi tingkat perusahaan memerlukan pemrosesan khusus dalam situasi berikut:
Saat file diunggah ke folder FTP
Ketika definisi dalam konfigurasi diubah
Saat draf dokumen diunggah
Ketika peristiwa sistem file lainnya terjadi
Ini semua adalah contoh pemberitahuan perubahan atau respons perubahan. Di versi Java sebelumnya (dan bahasa lainnya), polling adalah cara terbaik untuk mendeteksi peristiwa perubahan ini. Polling adalah jenis loop tak terbatas khusus: periksa sistem file atau objek lain, dan bandingkan dengan keadaan sebelumnya. Jika tidak ada perubahan, lanjutkan pemeriksaan setelah selang waktu sekitar beberapa ratus milidetik atau 10 detik. Hal ini berlangsung dalam putaran yang tak terbatas.
NIO.2 menyediakan cara yang lebih baik untuk melakukan deteksi perubahan. Daftar 1 adalah contoh sederhana.
Listing 1. Ubah mekanisme notifikasi di NIO.2
Copy kode kodenya sebagai berikut:
import java.nio.file.attribute.*;
importjava.io.*;
importjava.util.*;
importjava.nio.file.Path;
importjava.nio.file.Paths;
importjava.nio.file.StandardWatchEventKinds;
importjava.nio.file.WatchEvent;
importjava.nio.file.WatchKey;
importjava.nio.file.WatchService;
importjava.util.Daftar;
pengamat kelas publik{
publicstaticvoidmain(String[]args){
Paththis_dir=Jalur.mendapatkan(".");
System.out.println("Sekarang melihat direktori saat ini...");
mencoba{
WatchServicewatcher=this_dir.getFileSystem().newWatchService();
this_dir.register(pengamat,StandardWatchEventKinds.ENTRY_CREATE);
WatchKeywatckKey=watcher.take();
Daftar<WatchEvent<<64;>>events=watckKey.pollEvents();
for(TontonEventevent:events){
System.out.println("Seseorang baru saja membuat file'"+event.context().toString()+"'.");
}
}menangkap(Pengecualian){
System.out.println("Kesalahan:"+e.toString());
}
}
}
Kompilasi kode ini dan jalankan dari baris perintah. Di direktori yang sama, buat file baru, misalnya jalankan perintah touchexample atau copyWatcher.classexample. Anda akan melihat pesan pemberitahuan perubahan berikut:
Seseorang baru saja membuat file 'contoh1'.
Contoh sederhana ini menunjukkan cara mulai menggunakan fungsionalitas JavaNIO. Pada saat yang sama, ia juga memperkenalkan kelas NIO.2 Watcher, yang lebih langsung dan lebih mudah digunakan dibandingkan skema polling di I/O asli.
Hati-hati terhadap kesalahan ejaan
Saat Anda menyalin kode dari artikel ini, hati-hati terhadap kesalahan ejaan. Misalnya, objek StandardWatchEventKinds di Listing 1 berbentuk jamak. Bahkan dalam dokumentasi Java.net ejaannya salah.
Kiat
Mekanisme notifikasi di NIO lebih mudah digunakan dibandingkan metode polling lama, yang akan menyebabkan Anda mengabaikan analisis mendetail mengenai persyaratan tertentu. Saat pertama kali menggunakan pendengar, Anda perlu mempertimbangkan dengan cermat semantik konsep yang Anda gunakan. Misalnya, mengetahui kapan suatu perubahan akan berakhir lebih penting daripada mengetahui kapan perubahan itu dimulai. Analisis semacam ini perlu dilakukan dengan sangat hati-hati, terutama untuk skenario umum seperti memindahkan folder FTP. NIO adalah paket yang sangat kuat, tetapi juga memiliki beberapa "kesalahan" yang dapat menyebabkan kebingungan bagi mereka yang tidak terbiasa dengannya.
2. Selector dan asynchronous IO: Meningkatkan multiplexing melalui selector
Pendatang baru di NIO umumnya mengasosiasikannya dengan "input/output non-blocking". NIO lebih dari sekadar I/O non-pemblokiran, namun persepsi ini tidak sepenuhnya salah: I/O dasar Java memblokir I/O - artinya ia menunggu hingga operasi selesai - namun, I/O non-Pemblokiran atau asinkron adalah fitur NIO yang paling umum digunakan, tidak semua NIO.
I/O non-pemblokiran NIO digerakkan oleh peristiwa dan ditunjukkan dalam contoh mendengarkan sistem file di Listing 1. Ini berarti menentukan pemilih (panggilan balik atau pendengar) untuk saluran I/O, dan kemudian program dapat terus berjalan. Ketika suatu peristiwa terjadi pada pemilih ini - seperti menerima baris masukan - pemilih "bangun" dan mengeksekusi. Semua ini diimplementasikan melalui satu thread, yang sangat berbeda dari I/O standar Java.
Daftar 2 menunjukkan program jaringan multi-port echo-er yang diimplementasikan menggunakan pemilih NIO. Ini adalah program kecil yang dibuat oleh Greg Travis pada tahun 2003 (lihat daftar sumber daya). Sistem Unix dan mirip Unix telah lama menerapkan penyeleksi yang efisien, yang merupakan model referensi yang baik untuk model pemrograman berkinerja tinggi di jaringan Java.
Daftar 2.NIO pemilih
Copy kode kodenya sebagai berikut:
importjava.io.*;
importjava.net.*;
importjava.nio.*;
importjava.nio.channels.*;
importjava.util.*;
publicclassMultiPortEcho
{
port pribadi[];
privateByteBufferechoBuffer=ByteBuffer.mengalokasikan(1024);
publicMultiPortEcho(intports[])throwsIOException{
this.ports=port;
konfigurasi_selector();
}
privatevoidconfigure_selector()throwsIOException{
//Buat pemilih baru
Selectorselector=Selector.open();
//Buka daftarkan setiap port, dan daftarkan masing-masing port
//dengan pemilih
for(inti=0;i<ports.length;++i){
ServerSocketChannelssc=ServerSocketChannel.open();
ssc.configureBlocking(salah);
ServerSocketss=ssc.socket();
InetSocketAddressaddress=baruInetSocketAddress(ports[i]);
ss.bind(alamat);
SelectionKeykey=ssc.register(selector,SelectionKey.OP_ACCEPT);
System.out.println("Goingtolistenon"+ports[i]);
}
sementara(benar){
intnum=selector.pilih();
SetselectedKeys=selector.selectedKeys();
Iteratorit=Keys yang dipilih.iterator();
while(it.hasNext()){
SelectionKeykey=(SelectionKey)it.next();
if((key.readyOps()&SelectionKey.OP_ACCEPT)
==SeleksiKey.OP_ACCEPT){
//Terima koneksi baru
ServerSocketChannelssc=(ServerSocketChannel)kunci.saluran();
SocketChannelsc=ssc.accept();
sc.configureBlocking(salah);
//Tambahkan koneksi baru ke pemilih
SelectionKeynewKey=sc.register(selector,SelectionKey.OP_READ);
itu.hapus();
System.out.println("Mendapat koneksi dari"+sc);
}elseif((key.readyOps()&SelectionKey.OP_READ)
==KeyPilihan.OP_READ){
//Baca datanya
SocketChannelsc=(SocketChannel)kunci.saluran();
// data gema
intbytesEchoed=0;
sementara(benar){
echoBuffer.hapus();
intnumber_of_bytes=sc.read(echoBuffer);
if(jumlah_of_byte<=0){
merusak;
}
echoBuffer.flip();
sc.write(echoBuffer);
bytesEchoed+=jumlah_of_bytes;
}
System.out.println("Gema"+bytesEchoed+"dari"+sc);
itu.hapus();
}
}
}
}
staticpublicvoidmain(Stringargs[])throwsException{
if(args.panjang<=0){
System.err.println("Penggunaan:javaMultiPortEchoport[portport...]");
Sistem.keluar(1);
}
intports[]=newint[args.panjang];
for(inti=0;i<args.panjang;++i){
port[i]=Bilangan Bulat.parseInt(args[i]);
}
newMultiPortEcho(port);
}
}
Kompilasi kode ini dan mulai dengan perintah yang mirip dengan javaMultiPortEcho80058006. Setelah program ini berhasil dijalankan, jalankan telnet sederhana atau emulator terminal lain untuk terhubung ke antarmuka 8005 dan 8006. Anda akan melihat bahwa program ini menggemakan semua karakter yang diterimanya - dan melakukannya melalui thread Java.
3. Bagian: Janji dan Kenyataan
Di NIO, saluran dapat mewakili objek apa pun yang dapat dibaca dan ditulis. Perannya adalah menyediakan abstraksi untuk file dan soket. Saluran NIO mendukung serangkaian metode yang konsisten sehingga Anda tidak perlu memberikan perhatian khusus pada objek yang berbeda saat melakukan pengkodean, baik itu keluaran standar, koneksi jaringan, atau saluran yang digunakan. Fitur saluran ini diwarisi dari aliran di I/O dasar Java. Aliran menyediakan saluran pemblokiran yang mendukung I/O asinkron.
NIO sering direkomendasikan karena kinerjanya yang tinggi, namun lebih akurat karena waktu responsnya yang cepat. Dalam beberapa skenario, kinerja NIO lebih buruk daripada I/O Java dasar. Misalnya, untuk membaca dan menulis file kecil sekuensial sederhana, kinerja yang dicapai hanya dengan streaming mungkin dua hingga tiga kali lebih cepat daripada implementasi pengkodean berbasis saluran berorientasi peristiwa yang sesuai. Pada saat yang sama, saluran non-multipleks - yaitu saluran terpisah per thread - jauh lebih lambat dibandingkan beberapa saluran yang mendaftarkan penyeleksinya di thread yang sama.
Sekarang ketika Anda mempertimbangkan apakah akan menggunakan streaming atau saluran, coba tanyakan pada diri Anda pertanyaan-pertanyaan berikut:
Berapa banyak objek I/O yang perlu Anda baca dan tulis?
Apakah objek I/O yang berbeda berurutan, atau apakah semuanya harus terjadi pada waktu yang sama?
Apakah objek I/O Anda perlu bertahan dalam jangka waktu singkat atau seumur hidup proses Anda?
Apakah I/O Anda cocok untuk diproses dalam satu thread atau di beberapa thread berbeda?
Apakah komunikasi jaringan dan I/O lokal terlihat sama, atau apakah keduanya mempunyai pola yang berbeda?
Analisis seperti ini merupakan praktik terbaik ketika memutuskan apakah akan menggunakan aliran atau saluran. Ingat: NIO dan NIO.2 bukan pengganti I/O dasar, namun pelengkapnya.
4. Pemetaan memori - baja yang bagus digunakan pada bilahnya
Peningkatan kinerja paling signifikan di NIO adalah pemetaan memori. Pemetaan memori adalah layanan tingkat sistem yang memperlakukan bagian file yang digunakan dalam program sebagai memori.
Ada banyak efek potensial dari pemetaan memori, lebih dari yang bisa saya berikan di sini. Pada level yang lebih tinggi dapat membuat kinerja I/O akses file mencapai kecepatan akses memori. Akses memori seringkali lebih cepat dibandingkan akses file. Listing 3 adalah contoh sederhana dari peta memori NIO.
Listing 3. Pemetaan memori di NIO
Copy kode kodenya sebagai berikut:
importjava.io.RandomAccessFile;
importjava.nio.MappedByteBuffer;
importjava.nio.channels.FileChannel;
publicclassmem_map_example{
privatestaticintmem_map_size=20*1024*1024;
privatestaticStringfn="example_memory_mapped_file.txt";
publicstaticvoidmain(String[]args)throwsException{
RandomAccessFilememoryMappedFile=newRandomAccessFile(fn,"rw");
// Memetakan file ke dalam memori
MappedByteBufferout=memoryMappedFile.getChannel().map(FileChannel.MapMode.READ_WRITE,0,mem_map_size);
//Menulis ke dalamMemoryMappedFile
untuk(inti=0;i<mem_map_size;i++){
keluaran.put((byte)'A');
}
System.out.println("File'"+fn+"'isnow"+Integer.toString(mem_map_size)+"bytesfull.");
//Baca dari file yang dipetakan memori.
untuk(inti=0;i<30;i++){
Sistem.keluar.cetak((char)keluar.get(i));
}
System.out.println("/nReadingfrommemory-mappedfile'"+fn+"'iscomplete.");
}
}
Di Listing 3, contoh sederhana ini membuat file 20M example_memory_mapped_file.txt, mengisinya dengan karakter A, dan kemudian membaca 30 byte pertama. Dalam aplikasi praktis, pemetaan memori tidak hanya bagus dalam meningkatkan kecepatan mentah I/O, namun juga memungkinkan beberapa pembaca dan penulis berbeda untuk memproses gambar file yang sama pada waktu yang sama. Teknologi ini kuat tetapi juga berbahaya, tetapi jika digunakan dengan benar, kecepatan IO Anda akan meningkat beberapa kali lipat. Seperti kita ketahui bersama, operasi perdagangan Wall Street menggunakan teknologi pemetaan memori untuk mendapatkan keuntungan dalam hitungan detik atau bahkan milidetik.
5. Pengkodean dan pencarian karakter
Fitur terakhir NIO yang ingin saya jelaskan di artikel ini adalah charset, sebuah paket yang digunakan untuk mengonversi pengkodean karakter yang berbeda. Sebelum NIO, Java mengimplementasikan sebagian besar fungsi bawaan yang sama melalui metode getByte. charset populer karena lebih fleksibel dibandingkan getBytes dan dapat diimplementasikan pada level yang lebih rendah, sehingga menghasilkan performa yang lebih baik. Ini bahkan lebih berharga untuk mencari bahasa non-Inggris yang sensitif terhadap pengkodean, pengurutan, dan fitur bahasa lainnya.
Listing 4 menunjukkan contoh konversi karakter Unicode di Java ke Latin-1
Daftar 4.Karakter di NIO
Copy kode kodenya sebagai berikut:
Stringsome_string="Ini adalah string yang JavanativelystoresUnicode.";
Charsetlatin1_charset=Charset.forName("ISO-8859-1");
CharsetEncodelatin1_encoder=charset.newEncoder();
ByteBufferlatin1_bbuf=latin1_encoder.encode(CharBuffer.wrap(some_string));
Perhatikan bahwa Charset dan saluran dirancang untuk digunakan bersama, sehingga program dapat berjalan normal ketika pemetaan memori, I/O asinkron, dan konversi pengkodean dikoordinasikan.
Ringkasan: Tentu masih banyak lagi yang perlu diketahui
Tujuan artikel ini adalah untuk membiasakan pengembang Java dengan beberapa fitur terpenting (dan berguna) di NIO dan NIO.2. Anda dapat menggunakan landasan yang dibangun oleh contoh-contoh ini untuk memahami beberapa metode NIO lainnya; misalnya, pengetahuan yang Anda pelajari tentang saluran dapat membantu Anda memahami pemrosesan tautan simbolik dalam sistem file di Path NIO. Anda juga dapat merujuk ke daftar sumber daya yang saya berikan nanti, yang menyediakan beberapa dokumen untuk studi mendalam tentang API I/O Java yang baru.