Lima cara untuk memaksimalkan Java NIO dan NIO.2

Java NIO - paket API Input / Output Baru - diperkenalkan dengan J2SE 1.4 pada tahun 2002. Tujuan Java NIO adalah untuk meningkatkan pemrograman tugas intensif I / O pada platform Java. Satu dekade kemudian, banyak programmer Java masih tidak tahu cara memanfaatkan NIO dengan sebaik-baiknya, dan bahkan lebih sedikit lagi yang menyadari bahwa Java SE 7 memperkenalkan More New Input / Output APIs (NIO.2). Dalam tutorial ini Anda akan menemukan lima contoh mudah yang mendemonstrasikan keunggulan paket NIO dan NIO.2 dalam skenario pemrograman Java yang umum.

Kontribusi utama NIO dan NIO.2 ke platform Java adalah untuk meningkatkan kinerja di salah satu area inti pengembangan aplikasi Java: pemrosesan input / output. Tidak ada paket yang sangat mudah untuk dikerjakan, juga tidak ada API Input / Output Baru yang diperlukan untuk setiap skenario I / O Java. Namun, jika digunakan dengan benar, Java NIO dan NIO.2 dapat memangkas waktu yang diperlukan untuk beberapa operasi I / O umum. Itulah kekuatan super NIO dan NIO.2, dan artikel ini menyajikan lima cara yang relatif sederhana untuk memanfaatkannya:

  1. Ubah pemberi tahu (karena semua orang membutuhkan pendengar)
  2. Penyeleksi membantu multipleks
  3. Saluran - janji dan kenyataan
  4. Pemetaan memori - yang penting
  5. Pengkodean karakter dan pencarian

Konteks NIO

Bagaimana mungkin perangkat tambahan 10 tahun masih New paket Input / Output untuk Java? Alasannya adalah karena banyak programmer Java yang bekerja, operasi I / O Java dasar sudah lebih dari cukup. Sebagian besar pengembang Java tidak perlu mempelajari NIO untuk pekerjaan sehari-hari kami. Selain itu, NIO bukan hanya paket kinerja. Sebaliknya, ini merupakan kumpulan beragam fasilitas yang terkait dengan Java I / O. NIO meningkatkan kinerja aplikasi Java dengan semakin "mendekati logam" dari program Java, yang berarti bahwa NIO dan NIO.2 API memperlihatkan titik masuk sistem operasi (OS) tingkat rendah. Keuntungan dari NIO adalah bahwa hal itu secara bersamaan memberi kita kendali yang lebih besar atas I / O dan menuntut kita untuk lebih berhati-hati daripada yang kita lakukan dengan pemrograman I / O dasar. Aspek lain dari NIO adalah perhatiannya pada ekspresifitas aplikasi, yang akan kita mainkan dalam beberapa latihan berikut.

Memulai dengan NIO dan NIO.2

Banyak referensi bagus tersedia untuk NIO - lihat Sumberdaya untuk beberapa tautan terpilih. Untuk memulai dengan NIO dan NIO.2, dokumentasi Java 2 SDK Standard Edition (SE) dan dokumentasi Java SE 7 sangat diperlukan. Untuk menjalankan contoh di artikel ini, Anda harus menggunakan JDK 7 atau lebih tinggi.

Bagi banyak pengembang, pertemuan pertama dengan NIO mungkin terjadi selama pekerjaan pemeliharaan: aplikasi memiliki fungsionalitas yang benar tetapi lambat merespons, jadi seseorang menyarankan menggunakan NIO untuk mempercepatnya. NIO bersinar ketika digunakan untuk meningkatkan kinerja pemrosesan, tetapi hasilnya akan sangat terkait dengan platform yang mendasarinya. (Perhatikan bahwa NIO bergantung pada platform.) Jika Anda menggunakan NIO untuk pertama kalinya, Anda akan terbayar untuk mengukur dengan cermat. Anda mungkin menemukan bahwa kemampuan NIO untuk mempercepat kinerja aplikasi tidak hanya bergantung pada OS, tetapi juga pada JVM tertentu, konteks virtualisasi host, karakteristik penyimpanan massal, dan bahkan data. Pengukuran bisa jadi sulit untuk digeneralisasikan. Ingatlah hal ini terutama jika penerapan seluler termasuk di antara target Anda.

Dan sekarang, tanpa basa-basi lagi, mari kita jelajahi lima fasilitas penting NIO dan NIO.2.

1. Ubah pemberi tahu (karena semua orang membutuhkan pendengar)

Kinerja aplikasi Java adalah daya tarik umum bagi pengembang yang tertarik dengan NIO atau NIO.2. Dalam pengalaman saya, bagaimanapun, notifier perubahan file NIO.2 adalah fitur yang paling menarik (jika kurang dinyanyikan) dari New Input / Output API.

Banyak aplikasi kelas perusahaan perlu mengambil tindakan tertentu ketika:

  • File diunggah ke folder FTP
  • Definisi konfigurasi diubah
  • Draf dokumen diperbarui
  • Peristiwa sistem file lainnya terjadi

Ini semua adalah contoh pemberitahuan perubahan atau tanggapan perubahan. Di versi awal Java (dan bahasa lain), polling biasanya merupakan cara terbaik untuk mendeteksi peristiwa perubahan. Polling adalah jenis loop tak berujung tertentu: periksa sistem file atau objek lain, bandingkan dengan status terakhir yang diketahui, dan, jika tidak ada perubahan, periksa kembali setelah interval singkat, seperti seratus milidetik atau sepuluh detik . Lanjutkan pengulangan tanpa batas.

NIO.2 memberi kami cara yang lebih baik untuk mengekspresikan deteksi perubahan. Daftar 1 adalah contoh sederhana.

Daftar 1. Ubah pemberitahuan di NIO.2

import java.nio.file.attribute.*; import java.io.*; import java.util.*; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardWatchEventKinds; import java.nio.file.WatchEvent; import java.nio.file.WatchKey; import java.nio.file.WatchService; import java.util.List; public class Watcher { public static void main(String[] args) { Path this_dir = Paths.get("."); System.out.println("Now watching the current directory ..."); try { WatchService watcher = this_dir.getFileSystem().newWatchService(); this_dir.register(watcher, StandardWatchEventKinds.ENTRY_CREATE); WatchKey watckKey = watcher.take(); List
    
      events = watckKey.pollEvents(); for (WatchEvent event : events) { System.out.println("Someone just created the file '" + event.context().toString() + "'."); } } catch (Exception e) { System.out.println("Error: " + e.toString()); } } }
    

Kompilasi sumber ini, lalu luncurkan baris perintah yang dapat dieksekusi. Di direktori yang sama, buat file baru; Anda mungkin, misalnya touch example1, atau bahkan copy Watcher.class example1. Anda harus melihat pesan pemberitahuan perubahan berikut:

Someone just create the file 'example1'.

Contoh sederhana ini menggambarkan cara mulai mengakses fasilitas bahasa NIO di Java. Ini juga memperkenalkan Watcherkelas NIO.2 , yang jauh lebih lugas dan mudah digunakan untuk pemberitahuan perubahan daripada solusi I / O tradisional berdasarkan polling.

Perhatikan kesalahan ketik!

Berhati-hatilah saat Anda menyalin sumber dari artikel ini. Perhatikan, sebagai contoh, StandardWatchEventKindsobjek dalam Listing 1 dieja sebagai bentuk jamak. Bahkan dokumentasi Java.net melewatkannya!

Tip

Notifier NIO jauh lebih mudah digunakan daripada polling loop lama yang menggoda untuk mengabaikan analisis persyaratan. Tetapi Anda harus memikirkan semantik ini saat pertama kali menggunakan pendengar. Mengetahui kapan modifikasi file berakhir lebih berguna daripada mengetahui kapan itu dimulai, misalnya. Analisis semacam itu membutuhkan perhatian, terutama dalam kasus umum seperti folder drop FTP. NIO adalah paket yang kuat dengan beberapa "gotcha's" halus; itu bisa menghukum pengunjung biasa.

2. Selektor dan asynchronous I / O: Penyeleksi membantu multipleks

Pendatang baru di NIO terkadang mengaitkannya dengan "input / output non-blocking." NIO lebih dari sekadar I / O non-pemblokiran tetapi kesalahannya masuk akal: I / O dasar di Java memblokir - artinya ia menunggu hingga dapat menyelesaikan operasi - sedangkan non-pemblokiran, atau asinkron, I / O adalah salah satu fasilitas NIO yang paling sering digunakan.

I / O non-pemblokiran NIO adalah berbasis peristiwa , seperti yang ditunjukkan oleh pendengar sistem file di Daftar 1. Ini berarti bahwa pemilih (atau panggilan balik atau pendengar) ditentukan untuk saluran I / O, kemudian pemrosesan dilanjutkan. Ketika sebuah peristiwa terjadi pada selektor - ketika satu baris masukan tiba, misalnya - selektor "bangun" dan mengeksekusi. Semua ini dicapai dalam satu thread , yang sangat kontras dengan I / O Java pada umumnya.

Listing 2 demonstrates the use of NIO selectors in a multi-port networking echo-er, a program slightly modified from one created by Greg Travis in 2003 (see Resources). Unix and Unix-like operating systems have long had efficient implementations of selectors, so this sort of networking program is a model of good performance for a Java-coded networking program.

Listing 2. NIO selectors

import java.io.*; import java.net.*; import java.nio.*; import java.nio.channels.*; import java.util.*; public class MultiPortEcho { private int ports[]; private ByteBuffer echoBuffer = ByteBuffer.allocate( 1024 ); public MultiPortEcho( int ports[] ) throws IOException { this.ports = ports; configure_selector(); } private void configure_selector() throws IOException { // Create a new selector Selector selector = Selector.open(); // Open a listener on each port, and register each one // with the selector for (int i=0; i
    

Compile this source, then launch it from the command-line with an invocation such as java MultiPortEcho 8005 8006. Once the MultiPortEchoer is running, start up a simple telnet or other terminal emulator running against ports 8005 and 8006. You will see that the program echoes back characters it receives -- and does it in a single Java thread!

More NIO on JavaWorld

See the following JavaWorld articles for more background on the java.nio package APIs.

  • "Master Merlin's new I/O classes" (Michael T. Nygard, JavaWorld, September 2001)
  • "Use select for high-speed networking" (Greg Travis, JavaWorld, April 2003)

3. Channels: Promise and reality

In NIO, a channel can be any object that reads or writes. Its job is to abstract files and sockets. NIO channels support a consistent collection of methods, so it's possible to program without having special cases depending on whether stdout, a network connection, or some other channel is actually in use. Channels share this characteristic of the streams of Java's basic I/O. Streams provide blocking I/O; channels support asynchronous I/O.

While NIO is often promoted for its performance advantages, it's more precise to say it is highly responsive. In some cases NIO actually performs worse than basic Java I/O. For simple sequential reads and writes of small files, for instance, a straightforward streams implementation might be two or three times faster than the corresponding event-oriented channel-based coding. Also, non-multiplexed channels -- that is, channels in separate threads -- can be much slower than channels that register their selectors in a single thread.

The next time you need to define a programming problem in terms of dimensions pertinent to streams or channels, try asking the following questions:

  • How many I/O objects must you read and write?
  • Is there a natural sequence between different I/O objects or might they all need to happen simultaneously?
  • Do your I/O objects only last for a short interval or are they likely to persist during the lifetime of your process?
  • Is it more natural to do your I/O in a single thread or several distinct ones?
  • Is network traffic likely to look the same as local I/O or do the two have different patterns?

This sort of analysis is good practice for deciding when to use streams or channels. Remember: NIO and NIO.2 don't replace basic I/O; they just supplement it.

4. Memory mapping -- where it counts

The most consistently dramatic performance improvement in the use of NIO involves memory mapping. Memory mapping is an OS-level service that makes segments of a file appear for programming purposes like areas of memory.