IOはマルチスレッドとはあまり関連していませんが、NIOはアプリケーションレベルでのスレッドの使用方法を変更し、いくつかの実際的な困難を解決しました。 AIOは非同期IOであり、以前のシリーズも関連しています。ここでは、学習と録音のために、NIOとAIOを紹介する記事も書きました。
1。ニオとは何ですか
NIOは、古いストリームベースのI/Oメソッドとは反対の新しいI/Oの略語です。名前から判断すると、Java I/O標準の新しいセットを表します。 Java 1.4のJDKに組み込まれ、次の機能があります。
チャネルからのすべての読み取り操作はバッファーを通過する必要があり、チャネルはIOの抽象化であり、チャネルのもう一方の端は操作されたファイルです。
2。バッファー
Javaでのバッファーの実装。基本的なデータ型には、対応するバッファがあります
バッファーを使用する簡単な例:
パッケージテスト; Import java.io.file; import java.io.fileinputStream; Import java.nio.bytebuffer; Import java.nio.channels.filechannel; public class test {public static void main(string [] args)throws exception {fileinputStream fin = new fileInputStream(new File( "d://temp_buffer.tmp")); filechannel fc = fin.getChannel(); bytebuffer bytebuffer = bytebuffer.allocate(1024); fc.read(bytebuffer); fc.close(); bytebuffer.flip(); //読み取りと書き込み変換}}使用する手順が要約されています。
1。チャネルを取得します
2。バッファを適用します
3.チャネルとバッファーの間に読み取り/書き込み関係を確立します
4。閉じます
次の例は、NIOを使用してファイルをコピーすることです。
public static void niocopyfile(string resource、string destination)throws ioexception {fileinputStream fis = new fileInputStream(resource); fileoutputStream fos = new fileoutputStream(宛先); filechannel readchannel = fis.getChannel(); //ファイルチャネルFileChannel WriteChannel = fos.getChannel()を読む//ファイルチャンネルbytebuffer buffer = bytebuffer.allocate(1024); //(true){buffer.clear(); int len = readchannel.read(buffer); //データを読み取りますif(len == -1){break; //} buffer.flip()で読み取ります。 writechannel.write(バッファー); //ファイルに書き込み} readchannel.close(); writechannel.close(); }バッファには3つの重要なパラメーターがあります:位置、容量、制限
ここでは、容量と上限を区別する必要があります。たとえば、バッファに10kbの場合、10kbが容量です。 5kbファイルをバッファーに読んだ場合、上限は5kbです。
これらの3つの重要なパラメーターを理解するための例は次のとおりです。
public static void main(string [] args)スロー例外{bytebuffer b = bytebuffer.allocate(15); // 15-byte Buffer System.out.println( "limit =" + b.limit() + "capiought =" + b.capacity() + "position =" + b.position()); for(int i = 0; i <10; i ++){// 10バイトのデータb.put((byte)i); } system.out.println( "limit =" + b.limit() + "capiought =" + b.capacity() + "position =" + b.position()); b.flip(); //位置system.out.println( "limet =" + b.limit() + "capiought =" + b.capacity() + "position =" + b.position()); for(int i = 0; i <5; i ++){system.out.print(b.get()); } system.out.println(); System.out.println( "limit =" + b.limit() + "capiought =" + b.capacity() + "position =" + b.position()); b.flip(); System.out.println( "limit =" + b.limit() + "capiought =" + b.capacity() + "position =" + b.position()); }プロセス全体を図に示します。
この時点では、位置は0から10であり、容量と制限は変更されていません。
この操作は位置をリセットします。通常、バッファーを書き込みモードから読み取りモードに変換する場合、この方法を実行する必要があります。 Flip()操作は、現在の位置を0にリセットするだけでなく、現在の位置の位置に制限を設定します。
制限の意味は、どのデータが意味があるかを決定することです。言い換えれば、位置から制限へのデータは、最後の操作のデータであるため、意味のあるデータです。したがって、フリップ操作は、多くの場合、変換の読み取りと書き込みを意味します。
上記と同じ意味。
バッファーのほとんどの方法は、これらの3つのパラメーターを変更して、特定の機能を実現します。
パブリックファイナルバッファrewind()
位置ゼロとクリアマークを設定します
public final Buffer clear()
位置ゼロを設定し、容量のサイズに制限を設定し、フラグマークをクリアします
パブリックファイナルバッファフリップ()
最初に位置がある位置に制限を設定し、次に位置をゼロに設定し、通常は読み取りおよび書き込み変換中に使用されるフラグビットマークをクリアします
メモリにマッピングされたファイル
public static void main(string [] args)throws exception {randomaccessfile raf = new RandomAccessFile( "c://mapfile.txt"、 "rw"); filechannel fc = raf.getChannel(); //ファイルをメモリにマッピングするMappedByteBuffer mbb = fc.map(filechannel.mapmode.read_write、0、raf.length()); while(mbb.hasremaining()){system.out.print((char)mbb.get()); } mbb.put(0、(byte)98); //ファイルraf.close()を変更します。 }MappedByteBufferの変更は、ファイル自体の変更と同等であるため、動作速度は非常に高速です。
3。チャネル
マルチスレッドネットワークサーバーの一般的な構造:
シンプルなマルチスレッドサーバー:
public static void main(string [] args)スロー例外{serversocket echoserver = null; Socket ClientSocket = null; try {echoserver = new Serversocket(8000); } catch(ioException e){system.out.println(e); } while(true){try {clientsocket = echoserver.accept(); system.out.println(clientsocket.getRemoteSocketAddress() + "connect!"); tp.execute(new Handlemsg(clientsocket)); } catch(ioException e){system.out.println(e); }}}関数は、サーバーが読み取るたびにデータをクライアントに書き戻すことです。
ここで、TPはスレッドプールであり、Handlemsgはメッセージを処理するクラスです。
static class handlemsgは実行可能{情報の一部を省略しますpublic void run(){try {is = new inputStreamReader(clientsocket.getInputStream()); OS = new PrintWriter(ClientSocket.GetOutputStream()、true); // inputstream string inputline = nullからクライアントが送信したデータを読み取ります。 long b = system.currenttimemillis(); while((inputline = is.readline())!= null){os.println(inputline); } long e = system。 currenttimemillis();システム。 out.println( "spend:"+(e -b)+"ms"); } catch(ioexception e){e.printstacktrace(); }最後に{close resource}}}クライアント:
public static void main(string [] args)スロー例外{socket client = null; printwriter writer = null; bufferedreader reader = null; try {client = new Socket(); client.connect(new inetsocketAddress( "localhost"、8000)); writer = new PrintWriter(client.getOutputStream()、true); writer.println( "hello!"); writer.flush(); reader = new BufferedReader(new inputStreamReader(client.getInputStream())); system.out.println( "from server:" + reader.readline()); } catch(例外e){}最後に{//リソースクロージングを省略}}上記のネットワークプログラミングは非常に基本的であり、この方法を使用していくつかの問題があります。
クライアントごとに1つのスレッドを使用します。クライアントが遅延などの例外を経験した場合、スレッドは長い間占有される場合があります。データの準備と読み取りはこのスレッドにあるからです。現時点では、多くのクライアントがいる場合、多くのシステムリソースを消費する可能性があります。
解決:
非ブロッキングNIOを使用します(待機せずにデータを読む、データは動作する前に準備ができています)
NIO使用の効率を反映するため。
ここでは、最初に非効率的なクライアントをシミュレートして、ネットワークによる遅延をシミュレートします。
private static executorservice tp = executors.newcachedthreadpool();プライベート静的最終int sleep_time = 1000*1000*1000; public static class ecoclient runnable {public void run(){try {client = new Socket(); client.connect(new inetsocketAddress( "localhost"、8000)); writer = new PrintWriter(client.getOutputStream()、true); writer.print( "h"); locksupport.parknanos(sleep_time); writer.print( "e"); locksupport.parknanos(sleep_time); writer.print( "l"); locksupport.parknanos(sleep_time); writer.print( "l"); locksupport.parknanos(sleep_time); writer.print( "o"); locksupport.parknanos(sleep_time); writer.print( "!"); locksupport.parknanos(sleep_time); writer.println(); writer.flush(); } catch(例外e){}}}サーバー側出力:
支出:6000ms
支出:6000ms
支出:6000ms
支出:6001ms
支出:6002ms
支出:6002ms
支出:6002ms
支出:6002ms
支出:6003ms
支出:6003ms
なぜなら
while((inputline = is.readline())!= null)
それはブロックされているので、時間を待つのに費やされます。
NIOがこの問題に対処するために使用された場合はどうなりますか?
NIOの大きな機能の1つは、データの準備ができたら私に通知することです
チャネルはストリームに少し似ており、チャネルはファイルまたはネットワークソケットに対応できます。
セレクターは、チャネルを選択して何かを行うことができるセレクターです。
スレッドはセレクターに対応でき、セレクターは複数のチャネルを投票でき、各チャネルにはソケットがあります。
ソケットに対応する上記のスレッドと比較して、NIOを使用した後、スレッドは複数のソケットを投票できます。
セレクターがselect()を呼び出すと、クライアントがデータを準備したかどうかを確認します。データが準備ができていない場合、Select()がブロックされます。通常、NIOは非ブロッキングであると言われていますが、データの準備ができていない場合でもブロックされています。
データの準備ができたら、Select()を呼び出した後、SelectionKeyが返されます。 SelectionKeyは、セレクター上のチャネルのデータが準備されていることを示しています。
このチャネルは、データの準備ができたときにのみ選択されます。
このようにして、NIOは複数のクライアントを監視するためにスレッドを実装します。
シミュレートされたネットワークの遅延を備えたクライアントは、ソケットネットワークの遅延が発生した場合、データが準備ができておらず、セレクターが選択しないが、他の準備されたクライアントを選択するため、NIOの下のスレッドには影響しません。
SelectNow()とSelect()の違いは、SelectNow()がブロックしないことです。クライアントがデータを準備しない場合、SelectNow()はブロックせず、0を返します。クライアントがデータを準備すると、SelectNow()は準備されたクライアントの数を返します。
メインコード:
パッケージテスト; java.net.inetAddress;インポートjava.net.inetsocketaddress;インポートjava.net.socket; import java.nio.channels.selectionKey; Import java.nio.channels.selector; Import java.nio.channels.selector; Import java.nio.nio.nio.channels.serversockannel; java.nio.channels.socketchannel; Import java.nio.channels.spi.abstractselector; Import java.nio.channels.spi.selectorProvider; Import java.util.hashmap; Import java.util.iterator; Import java.util.util.util.til.util.util.util.util. java.util.set; Import Java.util.concurrent.executorservice; Import java.util.concurrent.executors;パブリッククラスMultithReadNioEchoserver {public static Map <socket、long> geym_time_stat = new Hashmap <Socket、long>(); class ecoclient {private linkedlist <bytebuffer> outq; eCoclient(){outq = new linkedlist <bytebuffer>(); } public linkedlist <bytebuffer> getOutputQueue(){return outq; } public void enqueue(bytebuffer bb){outq.addfirst(bb); }} class handlemsgはrunnable {selectionkey sk; Bytebuffer BB; public handlemsg(SelectionKey SK、bytebuffer bb){super(); this.sk = sk; this.bb = bb; } @Override public void run(){// todo auto-enerated method stub ecoclient ecoclient =(echoclient)sk.attachment(); eCoclient.Enqueue(BB); SK.INTERESTOPS(SelectionKey.op_read | SelectionKey.op_write); selector.wakeup(); }}プライベートセレクターセレクター; private executorservice tp = executors.newcachedthreadpool(); private void startServer()throws Exception {selector = selectorProvider.provider()。openSelector(); serversocketchannel ssc = serversocketchannel.open(); ssc.configureblocking(false); InetSocketAddress ISA = new InetSocketAddress(8000); ssc.socket()。bind(isa); //関心のあるイベントを登録してください。ここでは、ACCPETイベントSELECTIONKEY ACCEATKEY = SSC.REGISTER(selector、selectionKey.op_accept)に興味があります。 for(;;){selector.select(); readykeys = selector.selectedKeys()を設定します。 iterator i = readykeys.iterator(); long e = 0; while(i.hasnext()){selectionkey sk =(selectionkey)i.next(); I.Remove(); if(sk.isaceptable()){doaccept(sk); } else if(sk.isvalid()&& sk.isreadable()){if(!geym_time_stat.containskey(((socketchannel)sk .channel())。socket()) } doread(sk); } else if(sk.isvalid()&& sk.iswritable()){dowrite(sk); e = system.currenttimemillis(); long b = geym_time_stat.remove(((socketchannel)sk .channel())。socket()); system.out.println( "spend:" +(e -b) + "ms"); }}}} private void dowrite(selectionkey sk){// todo auto-enerated method stub socketchannelチャンネル=(socketchannel)sk.channel(); ecoclient ecoclient =(ecoclient)sk.attachment(); linkedlist <bytebuffer> outq = echoclient.getOutputQueue(); bytebuffer bb = outq.getlast(); try {int len = channel.write(bb); if(len == -1){disconnect(sk);戻る; } if(bb.remaining()== 0){outq.removelast(); }} catch(Exception E){// TODO:例外切断(SK)を処理します; } if(outq.size()== 0){sk.Interestops(selectionKey.op_read); }} private void doread(selectionkey sk){// dodo auto-eneratedメソッドStub Socketchannel Channel =(Socketchannel)Sk.Channel(); bytebuffer bb = bytebuffer.allocate(8192); int len; try {len = channel.read(bb); if(len <0){disconnect(sk);戻る; }} catch(Exception E){// TODO:例外切断(SK)を処理します;戻る; } bb.flip(); tp.execute(new Handlemsg(SK、BB)); } private void disconnect(selectionkey sk){// todo auto-fored method stub //ドライクロージング操作を省略} private void doaccept(selectionkey sk){// todo auto-fenerated method stub stub serversocketchannel server =(serversocketchannel)sk.channel(); Socketchannel ClientChannel; {clientchannel = server.accept(); clientchannel.configureblocking(false); selectionkey clientKey = clientchannel.register(selector、selectionkey.op_read); ecoclient echoclinet = new echoclient(); clientkey.attach(eCoclinet); inetAddress clientAddress = clientchannel.socket()。getinetAddress(); system.out.println( "" + clientAddress.gethostAddress()からの受け入れられた接続(); } catch(Exception e){// todo:Handle Exception}} public static void main(string [] args){// todo auto-enerated method stub multithreadnioechoserver echechoserver = new multithreadnioechoserver(); try {echoserver.startserver(); } catch(例外e){// dodo:例外を処理}}}}コードは参照のみであり、その主な機能は、さまざまなことをするためにさまざまなイベントに興味があることです。
以前にシミュレートされた遅延クライアントを使用する場合、今回の時間消費は2ms〜11msです。パフォーマンスの改善は明らかです。
要約:
1。NIOがデータを準備した後、処理のためにアプリケーションに引き渡します。データの読み取り/書き込みプロセスは、アプリケーションスレッドでまだ完了しており、待ち時間を別のスレッドに削除するだけです。
2。データの準備時間を保存します(セレクターが再利用できるため)
5。AIO
AIOの特徴:
1。読んだ後に私に通知します
2。IOは加速されませんが、読んだ後に通知されます
3.コールバック関数を使用して、ビジネス処理を実行します
AIO関連コード:
非同期serversocketchenchannel
server = asynchronousserversocketchannel.open()。bind(new inetsocketAddress(port));
サーバー上のAcceptメソッドを使用します
パブリックアブストラクト<a> void Accept(attachment、compleatehandler <asynchronoussocketchannel、?super a> handler);
Complete Handlerはコールバックインターフェイスです。クライアントが受け入れると、ハンドラーにあることを行います。
サンプルコード:
server.accept(null、new completehandler <asynchronoussocketchannel、object>(){final bytebuffer buffer = bytebuffer.allocate(1024); public void complete(asynchronoussocketchannel result、object attachment){system.out.out.println(swrew.out.currentln() {buffer.get(100、seconds.flip(); this); system.out.println()ここでは、未来を使用して即座のリターンを達成します。将来については、前の記事を参照してください
AIOを見るNIOの理解に基づいて、違いは、AIOが完了する前にコールバック関数を呼び出すために読み取りおよび書き込みプロセスを待つことです。
NIOは同期および非ブロッキングです
AIOは非同期で非ブロッキングです
NIOの読み取りおよび書き込みプロセスはまだアプリケーションスレッドで完了しているため、NIOは長い読み取りプロセスを持つ人には適していません。
AIOの読み取りおよび書き込みプロセスは、完了後にのみ通知されるため、AIOはヘビー級で長期的な読み取りプロセスタスクの能力があります。