プログラミングc 第9回 - cdr6275cdr6275.jp/home/wp-content/uploads/2013/06/... ·...

33
白石路雄 <[email protected]> プログラミングC 第9回 例外・スレッド 

Upload: others

Post on 22-May-2020

0 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: プログラミングC 第9回 - CDR6275cdr6275.jp/home/wp-content/uploads/2013/06/... · ここまでで他の人が作ったライブラリが投げる例 外を処理して使うことができるようになります。

白石路雄 <[email protected]>

プログラミングC 第9回例外・スレッド 

Page 2: プログラミングC 第9回 - CDR6275cdr6275.jp/home/wp-content/uploads/2013/06/... · ここまでで他の人が作ったライブラリが投げる例 外を処理して使うことができるようになります。

例外

例外:実行時に起きるエラーユーザーが入力した文字列を整数に変換するプログラムを実行した。しかし、ユーザーが整数以外の文字を入力してしまった。ファイルを処理するプログラムで指定したファイルがない。データベースを扱うプログラムでデータベースに接続できない。などなど。

2

Page 3: プログラミングC 第9回 - CDR6275cdr6275.jp/home/wp-content/uploads/2013/06/... · ここまでで他の人が作ったライブラリが投げる例 外を処理して使うことができるようになります。

例外を処理する (1)

例外を処理するプログラムfinally節はなくてもよい

3

try{ (例外が発生するかもしれない処理)}catch(Exceptionのクラス名 e){ (例外が発生した時の処理)}finally{ (例外の発生の有無に関わらず、必ず行う処理)}

Page 4: プログラミングC 第9回 - CDR6275cdr6275.jp/home/wp-content/uploads/2013/06/... · ここまでで他の人が作ったライブラリが投げる例 外を処理して使うことができるようになります。

例外を処理する (2)

例外の例Integer.parseInt()は、文字列が構文解析可能な整数型を含まない場合、NumberFormatExceptionを送出する(投げる)。

4

Page 5: プログラミングC 第9回 - CDR6275cdr6275.jp/home/wp-content/uploads/2013/06/... · ここまでで他の人が作ったライブラリが投げる例 外を処理して使うことができるようになります。

例外を処理する (3)

下記のプログラムで「3.5」と入力してみる

結果

5

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));String str = br.readLine();int value = Integer.parseInt(str);

Exception in thread "main" java.lang.NumberFormatException: For input string: "3.5"at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)at java.lang.Integer.parseInt(Integer.java:492)at java.lang.Integer.parseInt(Integer.java:527)at NumberFormatExceptionTest.main(NumberFormatExceptionTest.java:10)

Page 6: プログラミングC 第9回 - CDR6275cdr6275.jp/home/wp-content/uploads/2013/06/... · ここまでで他の人が作ったライブラリが投げる例 外を処理して使うことができるようになります。

例外を処理する (4)

NumberFormatExceptionが起きたときの処理を加える

6

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));String str = br.readLine();try{ int value = Integer.parseInt(str);}catch(NumberFormatException e){ System.out.println("Error!");}

Page 7: プログラミングC 第9回 - CDR6275cdr6275.jp/home/wp-content/uploads/2013/06/... · ここまでで他の人が作ったライブラリが投げる例 外を処理して使うことができるようになります。

メソッドでの例外の処理 (1)

キーボードからデータを読み出すクラスを作ってみる

7

public class KeyboardReader{ public int readInteger(){ BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String str = br.readLine(); int value = 0; try{ value = Integer.parseInt(str); } catch(NumberFormatException e){ System.out.println("Error!"); } return value; }} コンパイルできない

Page 8: プログラミングC 第9回 - CDR6275cdr6275.jp/home/wp-content/uploads/2013/06/... · ここまでで他の人が作ったライブラリが投げる例 外を処理して使うことができるようになります。

メソッドでの例外の処理 (2)

原因:StringクラスのreadLineメソッドはIOException

を投げる。

8

public class KeyboardReader{ public int readInteger(){ BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String str = br.readLine(); int value = 0; try{ value = Integer.parseInt(str); } catch(NumberFormatException e){ System.out.println("Error!"); } return value; }}

Page 9: プログラミングC 第9回 - CDR6275cdr6275.jp/home/wp-content/uploads/2013/06/... · ここまでで他の人が作ったライブラリが投げる例 外を処理して使うことができるようになります。

メソッドでの例外の処理 (2)

対応策1: 先ほどのように自分で処理するtry-catch-finallyを使う

2: メソッドの呼び出し側に処理を任せるthrows節を使う 

9

Page 10: プログラミングC 第9回 - CDR6275cdr6275.jp/home/wp-content/uploads/2013/06/... · ここまでで他の人が作ったライブラリが投げる例 外を処理して使うことができるようになります。

メソッドでの例外の処理 (3)

対応策1: 自分で処理する

10

public class KeyboardReader{ public int readInteger(){ BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); int value = 0; try{ String str = br.readLine(); value = Integer.parseInt(str); } catch(NumberFormatException e){ System.out.println("Error!"); } catch(IOException e){ System.out.println("Error!"); } return value; }}

Page 11: プログラミングC 第9回 - CDR6275cdr6275.jp/home/wp-content/uploads/2013/06/... · ここまでで他の人が作ったライブラリが投げる例 外を処理して使うことができるようになります。

メソッドでの例外の処理 (4)

対応策2: メソッドの呼び出し側に処理を任せるthrows節をメソッドの宣言部につける

11

public class KeyboardReader{ public int readInteger() throws IOException{ BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String str = br.readLine(); int value = 0; try{ value = Integer.parseInt(str); } catch(NumberFormatException e){ System.out.println("Error!"); } return value; }}

Page 12: プログラミングC 第9回 - CDR6275cdr6275.jp/home/wp-content/uploads/2013/06/... · ここまでで他の人が作ったライブラリが投げる例 外を処理して使うことができるようになります。

例外を定義する

ここまでで他の人が作ったライブラリが投げる例外を処理して使うことができるようになります。自分でライブラリを作る場合には、自分で例外を定義する必要が出てきます。その場合、Exceptionクラスを拡張したクラスを作り、throw

文で例外を投げます。

12

Page 13: プログラミングC 第9回 - CDR6275cdr6275.jp/home/wp-content/uploads/2013/06/... · ここまでで他の人が作ったライブラリが投げる例 外を処理して使うことができるようになります。

スレッド (1)

これまでのプログラムは基本的に上から順に実行された。処理の流れを増やすことができる。処理の流れをスレッドと呼ぶ。

13

これまでのプログラム

処理1処理2処理3

複数のスレッドによるプログラム

処理1

処理2 処理3

Page 14: プログラミングC 第9回 - CDR6275cdr6275.jp/home/wp-content/uploads/2013/06/... · ここまでで他の人が作ったライブラリが投げる例 外を処理して使うことができるようになります。

スレッド (2)

スレッドの作り方には2種類ある。1. Threadクラスを拡張したクラスを作る2. Runnableインターフェースを実装する

教科書では両方説明していますが、2の方法のほうが使い勝手がいいので授業では2だけ触れます。

14

Page 15: プログラミングC 第9回 - CDR6275cdr6275.jp/home/wp-content/uploads/2013/06/... · ここまでで他の人が作ったライブラリが投げる例 外を処理して使うことができるようになります。

スレッド (3)

Runnableインターフェースを実装したクラスを作り、別スレッドで実行したい処理をpublic void

run()メソッドの中に書きます

15

public class Life implements Runnable{ public void run(){ String[] s = {"Eat", "Play", "Nap"}; for(int i=0; i<6; i++){ try{ System.out.println("Life: " + s[i%3]); Thread.sleep(800); } catch(InterruptedException e){ } } }}

Page 16: プログラミングC 第9回 - CDR6275cdr6275.jp/home/wp-content/uploads/2013/06/... · ここまでで他の人が作ったライブラリが投げる例 外を処理して使うことができるようになります。

スレッド (4)

スレッドを実行するには、1. Runnableインターフェースを実装したクラスのインスタンスを作成する2. そのインスタンスを使ってThreadクラスのインスタンスを作る3. Threadクラスのインスタンスのstart()メソッドを呼び出す

16

Page 17: プログラミングC 第9回 - CDR6275cdr6275.jp/home/wp-content/uploads/2013/06/... · ここまでで他の人が作ったライブラリが投げる例 外を処理して使うことができるようになります。

スレッド (5)

17

public class Main{ public static void main(String[] args){ Life l = new Life(); Thread t = new Thread(l); t.start(); for(int i=0; i<8; i++){ try{ System.out.println("Main: Work"); Thread.sleep(600); } catch(InterruptedException e){ } } }}

Page 18: プログラミングC 第9回 - CDR6275cdr6275.jp/home/wp-content/uploads/2013/06/... · ここまでで他の人が作ったライブラリが投げる例 外を処理して使うことができるようになります。

スレッド (6)

18

実行結果

Main: WorkLife: EatMain: WorkLife: PlayMain: WorkLife: NapMain: WorkLife: EatMain: WorkMain: WorkLife: PlayMain: WorkLife: NapMain: Work

Page 19: プログラミングC 第9回 - CDR6275cdr6275.jp/home/wp-content/uploads/2013/06/... · ここまでで他の人が作ったライブラリが投げる例 外を処理して使うことができるようになります。

スレッド (7)

19

スレッドの実行順はJVMのスケジューラが決定し、制御できないスレッドの動きを制御するには、スレッドの終了を待つjoin()を使う必要がある

Page 20: プログラミングC 第9回 - CDR6275cdr6275.jp/home/wp-content/uploads/2013/06/... · ここまでで他の人が作ったライブラリが投げる例 外を処理して使うことができるようになります。

スレッド (8)

20

スレッドの終了を待つにはjoin()を使うpublic class Main{ public static void main(String[] args){ Life l = new Life(); Thread t = new Thread(l); t.start(); for(int i=0; i<8; i++){ try{ if(i==4) t.join(); System.out.println("Main: Work"); Thread.sleep(600); } catch(InterruptedException e){ } } }}

Page 21: プログラミングC 第9回 - CDR6275cdr6275.jp/home/wp-content/uploads/2013/06/... · ここまでで他の人が作ったライブラリが投げる例 外を処理して使うことができるようになります。

スレッド (9)

21

実行結果

Main: WorkLife: EatMain: WorkLife: PlayMain: WorkLife: NapMain: WorkLife: EatLife: PlayLife: NapMain: WorkMain: WorkMain: WorkMain: Work

Page 22: プログラミングC 第9回 - CDR6275cdr6275.jp/home/wp-content/uploads/2013/06/... · ここまでで他の人が作ったライブラリが投げる例 外を処理して使うことができるようになります。

同期 (1)

22

次のようなクラスを考えますレアカードを提供するサーバで、初期値100枚でconsumeCard()をすると1枚減ります

public class RareCardServer{ public int count = 100; public void consumeCard(){ int currrentCount = count; // データベースへの接続などにかかる時間を // シミュレートするため100ミリ秒停止 try{Thread.sleep(100);} catch(InterruptedException e){ } count = currentCount - 1; }}

Page 23: プログラミングC 第9回 - CDR6275cdr6275.jp/home/wp-content/uploads/2013/06/... · ここまでで他の人が作ったライブラリが投げる例 外を処理して使うことができるようになります。

同期 (2)

23

次のようなクラスを考えますレアカードを使うスレッド

public class RareCardConsumer implements Runnable{ private RareCardServer server; public RareCardConsumer(RareCardServer server){ this.server = server; } public void run(){ // 50枚適当に寝ながら使う for(int i=0; i<50; i++){ server.consumeCard(); try{Thread.sleep((int)(Math.random()*100));} catch(IntteruptedException e){ } } System.out.println("50枚つかったぜー"); }}

Page 24: プログラミングC 第9回 - CDR6275cdr6275.jp/home/wp-content/uploads/2013/06/... · ここまでで他の人が作ったライブラリが投げる例 外を処理して使うことができるようになります。

同期 (3)

24

RareCardConsumerを2つ作って同時に走らせてみるpublic class RareCardTest{ public static void main(String[] args){ RareCardServer s = new RareCardServer(); Thread t1 = new Thread(new RareCardConsumer(s)); Thread t2 = new Thread(new RareCardConsumer(s)); t1.start(); t2.start(); try{ t1.join(); t2.join(); } catch(IntteruptedException e){ } System.out.println("Serverに残ったカードの枚数:"+s.count); }}

Page 25: プログラミングC 第9回 - CDR6275cdr6275.jp/home/wp-content/uploads/2013/06/... · ここまでで他の人が作ったライブラリが投げる例 外を処理して使うことができるようになります。

同期 (4)

25

実行結果

2スレッドで50枚ずつ使ったのだからServerには1枚も残らないはず… (運営涙目)(Serverに残ったカードの枚数は実行するたび変化します)

50枚つかったぜー50枚つかったぜーServerに残ったカードの枚数:47

Page 26: プログラミングC 第9回 - CDR6275cdr6275.jp/home/wp-content/uploads/2013/06/... · ここまでで他の人が作ったライブラリが投げる例 外を処理して使うことができるようになります。

同期 (5)

26

問題な部分スレッド#1がconsumeCard()を実行するcountは100なので、currentCountを100にして、ちょっと処理する同時にスレッド#2がconsumeCard()を実行するcountは100なので、currentCountを100にして、ちょっと処理する

int currrentCount = count;try{Thread.sleep(100);} catch(InterruptedException e){ } count = currentCount - 1;

Page 27: プログラミングC 第9回 - CDR6275cdr6275.jp/home/wp-content/uploads/2013/06/... · ここまでで他の人が作ったライブラリが投げる例 外を処理して使うことができるようになります。

同期 (6)

27

問題な部分スレッド#1の先ほどの処理が終わったのでcountに99を書き込むスレッド#2の先ほどの処理が終わったのでcountに99を書き込む2枚使われてるはずなのに1枚しか減ってない (;_;)

int currrentCount = count;try{Thread.sleep(100);} catch(InterruptedException e){ } count = currentCount - 1;

Page 28: プログラミングC 第9回 - CDR6275cdr6275.jp/home/wp-content/uploads/2013/06/... · ここまでで他の人が作ったライブラリが投げる例 外を処理して使うことができるようになります。

同期 (7)

28

解決策:synchronizedキーワードをつけるそのメソッドを実行しているスレッドが1つであることを保証できる

public class RareCardServer{ public int count = 100; public synchronized void consumeCard(){ int currrentCount = count; // データベースへの接続などにかかる時間を // シミュレートするため100ミリ秒停止 try{Thread.sleep(100);} catch(InterruptedException e){ } count = currentCount - 1; }}

Page 29: プログラミングC 第9回 - CDR6275cdr6275.jp/home/wp-content/uploads/2013/06/... · ここまでで他の人が作ったライブラリが投げる例 外を処理して使うことができるようになります。

同期 (8)

29

GREE「ドリランド」でレアカード複製技が炸裂 (2012年2月)1枚レアカードを引いたら、2台の携帯を用意して、2つサブ垢作って、同時にカードをあげる処理をするそれぞれのサブ垢でカードをゲットできる→ヤフオクで転売 (よいこはまねしないでね)

レアカードだったらまだいいですけど、お金ならどういうことが起きますか?興味があるひとは「データベース 分離レベル」でgoogle

Page 30: プログラミングC 第9回 - CDR6275cdr6275.jp/home/wp-content/uploads/2013/06/... · ここまでで他の人が作ったライブラリが投げる例 外を処理して使うことができるようになります。

課題 (1)

30

整数を順に入力し、それらの合計を求めるプログラムExceptionTest.javaを書いてください。ただし、以下の条件を満たすものとします。整数を順に入力する。exitと入力されたら、その時点での和を出力してプログラムを終了する。「3.2」や「exit2」のように整数またはexit以外の入力があった場合には、「整数が正しいフォーマットで入力されませんでした。」と表示する。その場合でもプログラムが終了してはいけません。

Page 31: プログラミングC 第9回 - CDR6275cdr6275.jp/home/wp-content/uploads/2013/06/... · ここまでで他の人が作ったライブラリが投げる例 外を処理して使うことができるようになります。

課題 (2-1)

31

ファイルが更新されたかどうかを1秒間隔で調べるプログラムを書きます。FileTest.javaをダウンロードして、そこで使用されているFileWatchThreadクラスを完成させてください。動作は次のようになります。最初に指定するファイル名を入力します。スレッドの起動時にファイルのサイズを出力します。ファイルが更新されると、更新されたサイズを出力します。

Page 32: プログラミングC 第9回 - CDR6275cdr6275.jp/home/wp-content/uploads/2013/06/... · ここまでで他の人が作ったライブラリが投げる例 外を処理して使うことができるようになります。

課題 (2-2)

32

実行例は次のようになります。この例ではまずtest.txtという名前のファイルを作成しています。次にプログラムを実行しながら、test.txtファイルをエディタで更新しています。

監視するファイル名を指定してください> test.txtファイルをチェックしています。最初のサイズは3バイトです。ファイルが更新されました。サイズは7バイトです。ファイルが更新されました。サイズは9バイトです。

Page 33: プログラミングC 第9回 - CDR6275cdr6275.jp/home/wp-content/uploads/2013/06/... · ここまでで他の人が作ったライブラリが投げる例 外を処理して使うことができるようになります。

課題 (2-3)

33

ヒント

興味のある人は、ファイルの更新時刻を「2013年6月14日14時59分23秒」のように出力する方法について調べてみましょう。

// import java.io.*; として、以下のようにして// ファイルを表すクラスのインスタンスを作成します。File file = new File("test.txt");

// ファイルサイズの取得は以下のように行います。long size = file.length();

// ファイルの更新時刻(「エポック」からの経過時間をミリ秒単位で表したもの)の取得は以下のように行います。long lastModified = file.lastModified();