• 検索結果がありません。

text_10.dvi

N/A
N/A
Protected

Academic year: 2021

シェア "text_10.dvi"

Copied!
13
0
0

読み込み中.... (全文を見る)

全文

(1)

ソフト ウェア演習

C

(2)

目 次

第10章Java(5) { 例外処理,マルチスレッド 1

10.1

10

回演習内容 : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : :

1

10.2

例外処理: : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : :

1

10.2.1

例外 : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : :

1

10.2.2

例外処理: : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : :

2

10.3

マルチスレッド : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : :

4

10.3.1

Threadクラス : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : :

4

10.3.2

Runnableインタフェース : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : :

7

10.3.3

同期処理: : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : :

8

10.4

課題 : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : :

10

(3)

10

Java(5) {

例外処理

,

マルチスレッド

10.1

10

回演習内容

10

回では、プログラム実行中に発生するさまざ まな問題に対処するための例外処理と、複数の処理を 同時に実行させることのできるマルチスレッド についての演習を行います。 第

10

回目の演習内容は以下の通りです。 例外処理 マルチスレッド 10.2

例外処理

10.2.1

例外

プログラムの実行中、たとえば 、ある数値をゼロで除算した、配列の添字の指定が不正だった、指定し たファイルが見つからなかったなど 、さまざ まな問題が発生する場合があります。

Java

では、このような 問題を通知するために、例外(exception)と呼ばれるオブジェクトを実行時に生成します。 まず、以下のプログラムを実行してください。  // ExceptionTest.java // ExceptionTestクラス

public class ExceptionTest { // コンストラクタ ExceptionTest() { array() // arrayメソッド 呼出 } // arrayメソッド void array() { int ] ary = { 0, 1, 2, 3, 4 } // 配列変数定義 System.out.println( ary5] ) // これはエラーになる } // メイン メソッド

public static void main( String ] args ) { // ExceptionTestクラスのインスタンスを作成

ExceptionTest expTest = new ExceptionTest() }

}

 

このプログラムでは、まずメイン メソッド でExceptionTestクラスのインスタンスを作成しています。

ExceptionTestクラスのコンストラクタでは、arrayメソッド を呼び出しており、arrayメソッド では、

(4)

定義されていないのに対し 、定義されていない

6

番目の要素を参照しようとしているため、プログラムは エラーとなります。実際にプログラムを実行してみると、



$ java ExceptionTest

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5 at ExceptionTest.array(ExceptionTest.java:13) at ExceptionTest.<init>(ExceptionTest.java:7) at ExceptionTest.main(ExceptionTest.java:19)   と表示されます。つまり、ここでは定義されていない配列の要素を参照しようとしたとして、 ArrayIndexOutOfBoundsExceptionという例外オブジェクトが作成され 、異常終了したことを示していま す。このように、プログラム実行時になんらかの問題が生じた場合、例外オブジェクトが作成され通知し ますが 、これを「例外が投げられる」といいます。

Java

では、例外はExceptionクラスとして定義されており、また、さまざ まな種類の例外がException

クラスのサブ クラスとし て定義され ています。その中で 、最も頻繁に使用され 、最も重要なクラスは RuntimeExceptionクラスで、プログラム中に発生する頻度が高い例外を定義しています。 RuntimeExceptionクラスにはさらにサブ クラスがあり、その中の使用頻度の高いものを表

10.1

に示し ます。 表

10.1:

RuntimeExceptionのサブ クラス クラス 説明 ArrayIndexOutOfBoundsExcetion 配列の添字に実在しない要素番号を指定した ArithmeticException 算術例外が発生した(ゼロで除算したなど) ClassCastException 不正なキャスト操作を試みた NegativeArraySizeException 配列のサイズとして負の値を使用した NullPointerException 空のオブジェクトの変数やメソッド にアクセスしようとした NumberFormatException 文字列を数値に変換するときの文字列の形式が不正だった SecurityException セキュリティ違反のため操作が拒否された StringIndexOutofBoundsException 文字列のインデックスが文字列の領域をはみ出した

演習

10-1 以下のプログラムは

3

種類の例外が投げられる可能性がある。それぞれどのような場合にどのような例 外が投げられるかを挙げよ。  // ArgDiv.java public class ArgDiv {

public static void main( String ] args ) {

int val1 = Integer.parseInt( args0] ) // 文字列をint型に変換

int val2 = Integer.parseInt( args1] )

System.out.println( val1 + " / " + val2 + " = " + val1/val2 ) }

}

 

10.2.2

例外処理

通常はプログラム実行中に発生した例外は自動的に処理されますが 、

Java

では投げられた例外を、ユー

(5)

ことにより行います。構文は以下のようになります。



try {

// 例外が投げられる可能性のある処理

}

catch (ExceptionType1 param1) { // ExceptionType1に対応する例外処理

}

catch (ExceptionType2 param2) { // ExceptionType2に対応する例外処理

} ...

catch (ExceptionTypeN paramN) { // ExceptionTypeNに対応する例外処理 } finally { // finallyブロック }   まず、tryブロックでは、例外が投げられる可能性のある処理を記述します。次に、いくつかcatchブ ロックが続きます。ここでは、tryブロックで投げられる可能性のある例外オブジェクトを引数として与 え、実際に例外が投げられたときの処理を記述します。catchブロックは複数記述することができ、引数と して与えた例外オブジェクトに一致しない場合は次のcatchブロックに検索が進みます。finallyブロッ クは、例外発生の有無に関わらず処理される処理を記述します。このfinallyブロックは、省略可能です。 以下に、前のサンプルExceptionTest.javaに例外処理を加えた例を示します。  // ExceptionTest2.java // ExceptionTest2クラス

public class ExceptionTest2 { // コンストラクタ ExceptionTest2() { array() // arrayメソッド 呼出 } // arrayメソッド void array() { int ] ary = { 0, 1, 2, 3, 4 } // 配列変数定義 // try文の中に例外が投げられる可能性のある処理を書く try { System.out.println( ary5] ) // ここで例外が投げられる } // 投げられる可能性のある例外オブジェクトを指定 catch( ArrayIndexOutOfBoundsException be ) { System.out.println( "配列の添字が不正です" ) } // その他の例外の場合 catch( Exception e ) { System.out.println( "その他の例外" ) e.printStackTrace() // 例外情報を表示 }  

(

次のページに続く

)

(6)

// ここは必ず処理される finally { System.out.println( "finally内の処理" ) } System.out.println( "try文の外" ) } // メイン メソッド

public static void main( String ] args ) { // ExceptionTestクラスのインスタンスを作成

ExceptionTest2 expTest2 = new ExceptionTest2() } }   この例の場合、まず、ArrayIndexOutOfBoundsExceptionクラスのオブジェクトをcatchの引数とし ていますので、ArrayIndexOutOfBoundsExceptionが投げられた場合はこのブロックの処理が実行されま す。もし 、これ以外の例外が投げられた場合は次のcatch文を検索します。なお、

2

つ目のcatch文では、 Exceptionクラスのオブジェクトを引数としていますが 、この場合は全ての例外に一致することになりま す。また、このブロックのprintStackTraceメソッド は、Exceptionクラスの、例外発生時の情報を表 示するためのメソッド です。なお、これまでの例のような、RuntimeExceptionの場合は、特にプログラ ム内で例外処理を行わなくてもインタープ リタが処理してくれます。しかし 、RuntimeException以外の 例外を投げる可能性のあるメソッド1 を使用する場合はtry∼catch文で例外処理を行う必要があります。

演習

10-2 演習

10-1

で示したプログラムArgDiv.javaに、それぞれの例外に対応する例外処理を追加せよ。なお、 例外処理は、それぞれの例外に対応するメッセージを標準出力に出力することにより行うこと。 10.3

マルチスレッド

プログラムは、例えばmainメソッドの最初の処理から始まり、

1

文ずつ逐次的に実行されます。そして 終点を見つけると、そこで処理を終了します。プログラムの作りによっては複雑な処理の流れに見える場 合もありますが 、大局的に見ると通常は

1

本の流れとなっています。スレッド (Thread)とは、プログラ ムにおけるこのような処理の流れを意味します。マルチスレッド のプログラムとは、このスレッド を同時 に複数実行できる事を意味します。

Java

はマルチスレッド をサポートし 、

1

つのプログラム中で複数の処 理を同時に実行できます。例えば 、あるスレッドには画面の制御、またあるスレッドには音楽の処理といっ たように記述でき、作成された各スレッド はそれぞれ自分の処理を専属的に実行します。これは、

Java

の ように標準でマルチスレッド を想定していないプログラム言語では、作業の配分等をプログラマ自身が細 かく設定し 、複雑なプログラムを書かねばならないことと比較すると有効な機能といえます。 10.3.1 Thread

クラス

スレッド を作成するもっとも基礎的な方法は、java.lang.Threadクラスを継承する方法です。Thread クラスにはいくつかのメソッドが用意されています。ここでは、代表的なメソッド を紹介します。 1

Threadクラスのsleepメソッド や、BufferedReaderクラスのreadLineメソッド など 。それぞれのメソッドについては次節 以降で扱います。

(7)

Threadクラスのインスタンスに対してstartメソッドを呼び出すと、そのインスタンスのrunメソッド

を実行する新しいスレッドが作成されます。runメソッド は通常のメソッドとしても機能しますが 、main

メソッド のようにスレッド の処理が始まったときに自動的に呼ばれます。runメソッド の実行が終了した

時点でそのスレッド は消滅します。図

10.1

にこの概略を示します。

main() Thread t Thread u

u.start() t.start() run() run() 図

10.1:

スレッド また、

2

つのスレッドを作成する例を以下のプログラムThreadTest.javaに示します。  // MyThread.java

public class MyThread extends Thread { // Thread クラスを継承

String mes // メッセージ

long sleeptime // 中断時間(msec)

public MyThread( String mes, long sleeptime ) { // コンストラクタ

this.mes = mes

this.sleeptime = sleeptime }

public void run() { // runメソッド をオーバーライド

for( int i = 0 i < 10 i++ ) { System.out.println( mes+i ) try {

Thread.sleep( sleeptime ) // sleeptimeの間、処理を中断

} catch( InterruptedException ie ) { ie.printStackTrace() } } } }  

(8)

// ThreadTest.java public class ThreadTest {

public static void main( String ] args ) {

Thread t = new MyThread( "Hello Java. ", 1000 ) // MyThreadのインスタンス

Thread u = new MyThread( "Hello world. ", 2000 ) u.start() // startメソッド

t.start()

System.out.println( "Main thread is over." ) } }   このプログラムの実行結果は以下のようになります。  $ javac ThreadTest.java $ java ThreadTest Hello world. 0 Main thread is over. Hello Java. 0 Hello Java. 1 Hello world. 1 Hello Java. 2 Hello Java. 3 Hello world. 2 Hello Java. 4 Hello Java. 5 Hello world. 3 Hello Java. 6 Hello Java. 7 Hello world. 4 Hello Java. 8 Hello Java. 9 Hello world. 5 Hello world. 6 Hello world. 7 Hello world. 8 Hello world. 9   このプログラムではMyThreadクラスのインスタンスを

2

つ作成し 、startメソッドを呼び出します。作 成された各インスタンスでは、第

1

引数で与えられた文字列を表示した後、第

2

引数で指定された時間の 間処理を中断します。この処理をそれぞれ独立したスレッドで

10

回行います。また、このプログラムで、 sleepメソッド は、Threadクラスの静的メソッド で、引数で与えた時間

(

ミリ秒

)

だけ処理を中断します。 つまり、この場合、

1

秒毎に文字列を表示するスレッドと、

2

秒毎に文字列を表示するスレッドが同時に処 理されます。スレッド 毎に

CPU

を専属的に配分できないコンピュータでは、システムが各スレッド の処理 を

CPU

に適当に割り当てます。このため、このプ ログラムの実行結果はシステムの内部状態に依存し常 に同じ結果を出力するとは限りません。なお、sleepメソッド は、InterruptedExceptionを投げる可能 性があるため、try∼catch文による例外処理を行う必要があります。

演習

10-3

3

つのスレッド を持つプログラムを作成せよ。ここで、

1

つ目はアルファベット

(A

Z)

を順に

1

秒毎に 表示するスレッド、

2

つ目は数字

(0

9)

を順に

3

秒毎に表示するスレッド、

3

つ目はひらがな

(

あ∼ん

)

を 順に

0.5

秒毎に表示するスレッド とする。なお、ここでは表示するメソッドとしてSystem.out.print() を使用するものとする。

(9)

10.3.2 Runnable

インタフェース

Java

は単一継承であるため、Threadクラスを継承した場合は他のクラスを継承できません。例えば 、画 面にフレームを作成するスレッドを作りたい場合、JFrameクラス2 を継承する方法をとりますが 、これで はThreadクラスの継承はできません。このため、Runnableインタフェースが用意されており、これを実 装する事によりあるクラスを継承しつつ独立したスレッド として機能させる事ができます。  // MyRunnable.java

public class MyRunnable implements Runnable { // Runnableインタフェースを実装

String mes // メッセージ

long sleeptime // 中断時間(msec)

public MyRunnable( String mes, long sleeptime ) { // コンストラクタ

this.mes = mes

this.sleeptime = sleeptime }

public void run() { // runメソッド をオーバーライド

for( int i = 0 i < 10 i++ ){ System.out.println( mes+i ) try {

Thread.sleep( sleeptime ) // sleeptimeの間、処理を中断

} catch( InterruptedException ie ) { ie.printStackTrace() } } } }    // RunnableTest.java

public class RunnableTest {

public static void main( String ] args ) {

Runnable cmd1 = new MyRunnable( "Hello Java. ", 1000 ) // MyRunnableのインスタンス

Runnable cmd2 = new MyRunnable( "Hello world. ", 2000 ) Thread t = new Thread( cmd1 ) // Threadのインスタンス

Thread u = new Thread( cmd2 ) t.start() // startメソッド

u.start()

System.out.println( "Main thread is over." ) }

}

 

Threadクラスを継承する方法と同様に、複数のスレッドを作成することが確認できます。このように、ス

レッドでの処理をRunnableインタフェースを実装したクラスのrunメソッドに記述し 、それをRunnable

クラスのインスタンスとして作成し 、さらにそれをThreadクラスのコンストラクタに引数として渡すこ とで、他のクラスを継承したオブジェクトを処理するスレッド を作成することができます。

演習

10-4 演習

10-3

を、Runnableインタフェースを実装する方法で書き換えよ。 2 JFrameクラスについては、次回以降の演習で行います。

(10)

10.3.3

同期処理

スレッドはそれぞれ独立して処理を実行するので、どのスレッドがどのオブジェクトのデータをどの時 点で参照あるいは利用するかといったことは予測できません。あるスレッドが 、あるオブジェクトのデー タを変更している最中に、他のスレッドがそのオブジェクトのデータを参照したとすると正しいデータの 参照ができない場合があります。処理を厳密に行うためには、複数のスレッド 間で処理のタイミングを取 り合う必要があり、これを同期をとるといいます。

Java

では、あるメソッド に対して同時に複数のスレッドがそのメソッドを参照しないようにするための 予約語synchronizedが用意されています。これを宣言したメソッドは同時に

1

つのスレッドからのみア クセスを受け付けます。ここで、もし複数のスレッドが 、synchronized宣言されたメソッド を参照しよ うとした場合、

1

つのスレッド のみがアクセスできるので他のスレッド はアクセス中のスレッドがそのメ ソッド へのアクセスを終了するまで待機状態になります。

Java

では、これらの待機状態のスレッド を連続 的に監視しており、synchronized宣言されたメソッド を持つオブジェクトのデータの状態をプログラマ が管理する必要はありません。  // Increment.java public class Increment {

int v

void calc( String s, long sleeptime ) { // インクリメント用のメソッド

for( int i = 0 i < 10 i++ ) { v++

System.out.println( v + " :" + s ) try {

Thread.sleep( sleeptime ) // sleeptimeの間、処理を中断

} catch( InterruptedException ie ) { ie.printStackTrace() } } } }    // ExecIncrement.java

public class ExecIncrement implements Runnable { Increment inc // Incrementのインスタンス

String t_name // スレッド の名前

long sleeptime // 中断時間

ExecIncrement( Increment inc, String t_name, long sleeptime ) { // コンストラクタ

this.inc = inc this.t_name = t_name this.sleeptime = sleeptime }

public void run() { // runメソッド をオーバーライド

inc.calc( t_name, sleeptime ) }

}

(11)

// SyncTest.java

// Incrementクラスのフィールドvの値を2つのスレッド で加算し 、20にする

public class SyncTest {

public static void main( String ] args ) { Increment inc = new Increment()

Thread t1 = new Thread( new ExecIncrement( inc, "Thread 1", 1000 ) ) Thread t2 = new Thread( new ExecIncrement( inc, "Thread 2", 1500 ) ) t1.start() t2.start() } }   この例のIncrementクラスのcalcメソッド は、フィールド vを

10

回インクリメントするメソッド です。 ExecIncrementクラスは、スレッドを作成するために、Runnableインタフェースを実装したクラスと して作成しています。 SyncTestクラスでは、Incrementクラスのインスタン ス

1

つに対し 、

2

つのスレッド を作成し 、実行 させています。

2

つのスレッドが同時に動いているので、

2

つのスレッドが入り混じりながらフィールド v をインクリメントする結果となります。そのため、処理系によっては

2

つのスレッドが同時にインクリメ ントしてしまう可能性があり、もし同時にインクリメントしてしまうと、期待通りの結果にはならない場 合があります。それを防ぐ ため、Incrementクラスのcalcメソッド をsynchronized宣言し 、複数のス レッドが同時にアクセスできないようにします。Incrementクラスのcalcメソッド を



synchronized void calc( String s, long sleeptime ) {

  と書き換えて実行してみてください。この場合、

1

つのスレッドが

10

までインクリメントした後に、もう

1

つのスレッドが

20

までインクリメントするようになります。

演習

10-5 演習

10-4

を、表示中にそれぞれの文字種が混ざらないように改造せよ。たとえば 、アルファベットを全 て表示させてから数字を表示させ 、数字を全て表示させてからひらがなを表示させるようにする。なお、 このとき、表示する文字種の順序は問わないものとする。

(12)

10.4

課題

必須課題については必ずレポートを提出してください。自由課題にもぜひ挑戦してみましょう。プログ ラムを作成する課題については、作成したソースプログラムおよびその実行に必要なソースプログラムと、 その実行結果を添付してください。また、作成したウィンド ウの画面イメージも添付してください。 2 必須課題

1.

以下のプログラムは

3

種類の例外が投げられる可能性がある。それぞれどのような場合にどのような 例外が投げられるかを挙げ(演習10-1)、それぞれの例外に対応する例外処理をプログラムに追加せ よ。なお、例外処理は、それぞれの例外に対応するメッセージを標準出力に出力することにより行う こと。(演習10-2)  // ArgDiv.java public class ArgDiv {

public static void main( String ] args ) {

int val1 = Integer.parseInt( args0] ) // 文字列をint型に変換

int val2 = Integer.parseInt( args1] )

System.out.println( val1 + " / " + val2 + " = " + val1/val2 ) } }  

2. 3

つのスレッドを持つプログラムを作成せよ。ここで、

1

つ目はアルファベット

(A

Z)

を順に

1

秒毎に表 示するスレッド、

2

つ目は数字

(0

9)

を順に

3

秒毎に表示するスレッド、

3

つ目はひらがな

(

あ∼ん

)

を順 に

0.5

秒毎に表示するスレッドとする。なお、ここでは表示するメソッドとしてSystem.out.print() を使用するものとする。(演習10-3)

3.

必須課題

2

を、Runnableインタフェースを実装する方法で書き換えよ。(演習10-4)

4.

必須課題

3

を、表示中にそれぞれの文字種が混ざらないように改造せよ。たとえば 、アルファベット を全て表示させてから数字を表示させ、数字を全て表示させてからひらがなを表示させるようにする。 なお、このとき、表示する文字種の順序は問わないものとする。(演習10-5) 2 自由課題

1. Java

では、発生した例外を呼び出し元のメソッド に知らせることができ、呼び出し元のメソッド でも 例外処理を行うことができる。この機能について調べ、テキスト内のExceptionTest2.javaについ て、ExceptionTest2コンストラクタでも例外処理を行うようにプログラムを変更せよ。

2. Java

では、独自の例外を作成し 、処理することができる。この機能について調べ、Math.random()メ ソッド を利用して

0

99

の乱数を

10

個発生させ、乱数が

10

以下の場合に例外を発生させるプログラ ムを作成せよ。

3.

マルチスレッドにおけるデッド ロックとはどのような状態のことか、どのようなときに起こり得るか、 また、それを防ぐにはどのようにすればよいか調べて述べよ。

参考文献

1) 「

3

日で解る

Java

,

共立出版

,

桑原 恒夫 著

2) 「

JAVA

実践プログラミング 」

,

オライリージャパン

, Patrick Niemeyer & Joshua Peck

,

安藤進 訳

(13)

明広 訳

4) 「

Java

プログラムデザイン 」

,

ソフトバンク

,

戸松豊和 著

5) 「

JAVA

プログラムクイックリファレンス」

,

オライリージャパン

, David Flanagan

,

豊福剛 訳 6) 「

JAVA

入門」

,

インプレス

, Nathan Gurewich & Ori Gurewich

,

石川和也 訳

7) 「

Internet Language Java

入門」

,

技術評論社

,

河西朝雄 著

8) 「

Java

言語入門」

,

プレンティスホール

, Laura Lemay, & Charles L. Perkins

,

武舎広幸

, &

久野禎

, &

久野靖 訳

9) 「基礎からの

JAVA2

,

工学社

,

足立理一 著

参照

関連したドキュメント

奥付の記載が西暦の場合にも、一貫性を考えて、 []付きで元号を付した。また、奥付等の数

そこで本研究ではまず、乗合バス市場の変遷や事業者の経営状況などを考察し、運転手不

荒天の際に係留する場合は、1つのビットに 2 本(可能であれば 3

この P 1 P 2 を抵抗板の動きにより測定し、その動きをマグネットを通して指針の動きにし、流

Q7 建設工事の場合は、都内の各工事現場の実績をまとめて 1

平成 27

基準の電力は,原則として次のいずれかを基準として決定するも

平成 27