UIの処理を行うスレッドはswingではEDT(Event Dispatch Thread)と呼ばれます。EDTでは時間のかかる処理を行ってはいけないという原則があるので、そういう処理は別スレッドで行うという方針になります。以下サーバー処理を行うスレッドをサーバースレッドと呼びます。
サーバースレッドから画面の状態を変える場合はSwingUtilities#InvokeLater/InvokeAndWaitなどを使うと簡単に行えます。逆にEDTから長期間動作しっぱなしのサーバースレッドのようなものへ任意のタイミングで指示をしようとすると若干面倒かも知れません。それ用の便利なクラスもあると思いますが初めてマルチスレッド処理を学ぶケースでは前提知識が少なくその機能の説明自体がわからないこともありそうです。私などはそういったクラスのAPIリファレンスの説明を読んでも最初はちんぷんかんぷんでした。ですのでネットにある記事を手掛かりにするとよいと思います。まずは基本となるThread, synchronizedや複数のスレッドから安全に更新できるデータをサポートするクラス(AtomicXXX)のあたりから学ばれるとよいと思います。なおEDTからサーバースレッドへコマンドを渡す素朴な例を以下に挙げます。Startボタンでサーバー開始、Stopボタンでサーバーを終わらせるといった感じです。(コンパイルしてないので綴りミスなどあるかもしれませんが雰囲気はつかめるかと思います)
java
1// UIのクラス(EDTでのみアクセス可)
2class UI extends JPanel {
3 Server server;
4 JButton btnStart;
5 JButton btnStop;
6
7 UI() {
8 btnStart = new JButton(new AbstractAction("Start") {
9 @Override public void actionPerformed(ActionEvent ev) {
10 btnStart.setEnabled(false);
11 server = new Server();
12 server.start();
13 }
14 });
15 btnStop = new JButton(new AbstractAction("Stop") {
16 @Override public void actionPerformed(ActionEvent ev) {
17 btnStop.setEnabled(false);
18 server.push(Server.Command.Stop);
19 }
20 });
21 btnStop.setEnabled(false);
22 ...
23 }
24 ...
25}
26
27// サーバースレッドで仕事を行うためのクラス
28public class Server implements Runnable {
29 // サーバーへのコマンド
30 public enum Command {
31 None,
32 Stop,
33 ...
34 }
35 // UIからの指示を受け取るキュー。
36 // LinkedListはスレッドセーフでないのでsynchronizedを用い同期した上でアクセス。
37 // (スレッドセーフなクラスもあるのでそれを使うと同期は不要)
38 Deque<Command> commands = new LinkedList<>();
39 UI ui;
40
41 public Server(UI ui) { this.ui = ui; }
42
43 public void start(UI ui) {
44 Thread t = new Thread(this);
45 t.setDaemon(true); // このスレッドが残っていても画面を閉じればアプリが終わるように
46 t.start();
47 }
48
49 // UIスレッドからコマンドをキューに入れる(ごく短い時間で終わる)
50 public void push(Command c) {
51 synchronized (commands) { commands.push(c); }
52 }
53
54 // コマンドをキューから取り出す(ごく短い時間で終わる)
55 private void pop() {
56 synchronized (commands) {
57 return commands.isEmpty() ? Command.None : commands.pop();
58 }
59 }
60
61 // サーバースレッドの処理
62 @Override public void run() {
63 // サーバーが開始したのでEDT上でStopボタンをenableにする
64 SwingUtilities.invokeLater(() -> ui.btnStop.setEnabled(true));
65 Socket serverSocket = ...;
66 serverSocket = setSoTimeout(1000); // 1秒でTimeoutにし、UIからのコマンドをチェック
67 LOOP:
68 for (;;) {
69 try {
70 // UIから指示があったらそれに従う
71 switch (pop()) {
72 case None: break;
73 case Stop: break LOOP;
74 ...
75 }
76 Socket sock = serverSocket.accept();
77 // クライアントの相手をする
78 ...
79 } catch (SocketTimeoutException ex) {
80 // タイムアウトしたらまたループの最初から
81 } catch (InterruptedException ex) {
82 break LOOP; // accept実行中に割り込まれたら行儀よく終わる
83 }
84 }
85 ...後始末
86 // 再度開始できるようにEDT上でStartボタンをenableにする
87 SwingUtilities.invokeLater(() -> ui.btnStart.setEnabled(true));
88 }
89}
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2016/10/10 11:58