あらかじめスレッドを作成しておくサーバ


小さなトランザクションが多数発生する場合、すなわち、 クライアントとは少量のデータを交換する軽い通信を行うだけだが 大量のクライアントから頻繁に接続要求がある場合、 各クライアントが接続してくる度に新しいスレッドを 生成していては反応が遅れてしまう場合があります。

同時に処理しなければならないクライアントの数があらかじめ 予測できるのであれば、それだけの個数のスレッドを前もって 生成しておくことでこの問題に対処できます


スレッドがそれぞれaccept()を実行する方法

この場合、各クライアントからの要求に対応するサーバスレッドそれぞれが、 クライアントからの接続を受け付けるために自分で ServerSocketを使ってaccept()を実行する必要があります。 しかしServerSocketクラスのaccept()メソッドはスレッドセーフ ではないので、同時に複数のスレッドが同じServerSocketに対して accept()メソッドを実行してしまっては正しく動作しません。 そのため、各スレッドがaccept()する部分で synchronized を指定して、 一度にひとつのスレッドしかaccept()呼び出しができないようにします。 accept()を実行したスレッドはクライアントからの接続があるとaccept()を 終了してsynchronized領域から抜けます。 すると ServerSocketに対するロックが解除されるのを待っていたスレッドが 動作を再開してロックを取るために競争します。運よくロックを取得できた スレッドがsynchronized領域に入ることができてaccept()メソッドを実行します。 ロックを取得できなかったその他のスレッドはふたたびロックが開放されるのを 待ってwait状態になります。

ここではクライアントの世話をするために EchoHandler2という クラス(Runnableをimplementsしている)を作成しています。




RunEchoServerThreaded2.javaの実行例(サーバ)
$ javac RunEchoServerThreaded2.java 
$ java RunEchoServerThreaded2 8888 2 
0: starts
0: waiting
1: starts
1: waiting
0: accept Socket[addr=/127.0.0.1,port=50955,localport=8888]
1: accept Socket[addr=/127.0.0.1,port=50956,localport=8888]
0: data=apple
1: data=red
0: data=peach
1: data=pink
1: closed Socket[addr=/127.0.0.1,port=50956,localport=8888]
1: waiting
1: accept Socket[addr=/127.0.0.1,port=50957,localport=8888]
1: data=tiger
0: data=melon
1: data=lion
1: closed Socket[addr=/127.0.0.1,port=50957,localport=8888]
1: waiting
0: data=orange
0: closed Socket[addr=/127.0.0.1,port=50955,localport=8888]
0: waiting
実行例(クライアント0)
$ java RunTelnetClient localhost 8888 
apple 
apple     ←サーバからの返事
peach 
peach     ←サーバからの返事
melon 
melon     ←サーバからの返事
orange 
orange     ←サーバからの返事
           ←Ctl-Z  Return で終了
実行例(クライアント1)
$ java RunTelnetClient localhost 8888 
red 
red     ←サーバからの返事
pink 
pink     ←サーバからの返事
           ←Ctl-Z  Return で終了
実行例(クライアント2)
$ java RunTelnetClient localhost 8888 
tiger 
tiger     ←サーバからの返事
lion 
lion     ←サーバからの返事
           ←Ctl-Z  Return で終了