Page 116 of 129

9.インターフェース

9章 インターフェース
1.インターフェースとは
 ひし形クラスと長方形クラスの両方の特徴を継承した正方形クラスのようなものを多重継承という。しかし、Javaでは多重継承の概念は取り入れられていません。これを解決するためにインターフェースという機能をサポートしている。
 インターフェースとは、定数とメソッドの宣言だけを集めたものである。宣言だけで実際のメソッド本体などは実装しない。あるクラスを作ろうとした場合に、「インターフェースをインプリメントする」と、そのクラスはインターフェースで宣言したメソッドと変数を必ず実装しなければならなくなる。
 つまり、MyInterfaceというインタフェースを作り、その中で「func1」というメソッドを宣言したとする。そして、MyClassを作ろうとした場合にMyInterfaceをインプリメントした場合、MyClassの中にはかならずfunc1というメソッドを実装する必要がある。
 一般的に継承は継投を表し、インターフェースは機能を表す。しかし、具体的な機能を決めるものではなく、このような機能があることを明示するためのものである。したがって、インターフェースはJavaのポリモーフィズムを実現する1つの手段であるといえる。
 ここで、例を示す。犬クラスは哺乳類クラスのサブクラスである。犬にも秋田犬クラス、ゴールデンレトリバークラスやブルドッククラスなどがサブクラスとして存在するかもしれない。これらは動物の種類の系統でサブクラスを作ってきている。また犬には、盲導犬や麻薬捜査犬、救助犬といった分類もあるが、これらはそれぞれ、盲人を誘導する機能を有するとか麻薬を見つけるとか救助するといった機能を表す。つまり、犬の種類とは関係ない。したがって、これらの機能)(インタフェース)をインプリメントした犬クラスが盲導犬クラスというように定義できるのである。
 今の世の中では盲導象や盲導猫というのは存在しないが、もしかすると将来盲導猫というものができたとすれば、猫クラスのサブクラスで、盲導インターフェースをインプリメントして盲導猫クラスを作ることができる。  
  
2 インターフェースの宣言 
interface interfaceName{
    type name = value
    type name = value                                           // 変数
    type method( paramType name… );           // メソッド
}
   

interface Shape2D{
     // 面積を計算する
  double getArea( );
}

 上記のようなShepe2Dインターフェースをインプリメントしたクラスは、必ずgetAreaメソッドを実装しなければならない。下記にその使用例を示す。 
 // 図形クラス
abstract class Shape{
    abstract void Display( );
}

// 円クラス
class Circle extends Shape implements Shape2D{
    int x;
    int y;
    int r;

    Circle ( int x, int r ){
        this.x = x;
        this.x = y;
        this.x = r;

    }

    void Display( ){
         System.out.println("x=" + x + ", y=" + y + ", r=" + r);
    }

    // 面積を計算する
    public double getArea( ){
         return r*r*Math.PI ;    // Math.PI:円周率
    }
}

    // 長方形クラス
class Rect extends Shape implements Shape2D{
        int x1;
       int y1;
       int x2;
       int y2;

     Rect ( int x1, int y1, int y2 ){
               this.x1 = x1;
               this.y1 = y1;
               this.x2 = x2;
               this.y2 = y2;
       } 

       void Display( ){
              System.out.println("x1=" + x1 + ", y1=" + y1 + ", x2=" + y2 + ", y2=" + y2);
       }

       // 面積を計算する
       public double getArea( ){
            // Math.abs : 絶対値を求めるメソッド
              return Math.abs(x1-x2) * Math.abs(y1-y2);
       }
}

 Shapeクラスは図形クラスであり、図形というからには何らかの形で表示する必要がある。表示方法は円や三角形、長方形など図形によって異なる。そこで、Shapeクラスでは実装できずサブクラスで実装する必要があるため、abstractになっている。
 Shapeクラスのサブクラスとして、ここではCircleクラス(円クラス)とRectクラス(長方形クラス)を定義している。ここでは、そのクラスの機能として面積を計算するためのShape2Dインターフェースをインプリメントしている。
  
3 インターフェースの参照 
Test.java
public class Test{
      public static void main(String args[ ]){
         Shape2D array[ ] = new Shape2D[5];

          array[0] = new Circle(5,3,2);
          array[1] = new Rect(5,3,20,7);
          array[2] = new Circle(3,1,1);
          array[3] = new Rect(0,0,3,3);
          array[4] = new Circle(-5,2,2);
 
          double area = 0;    // 面積 
          area += array[0].getArea( );
          area += array[1].getArea( );
          area += array[2].getArea( );
          area += array[3].getArea( );
          area += array[4].getArea( );
  

          System.out.println("面積の合計="+area);
      }
}
  

C:\java>java Test
面積の合計=97.27433388230814

C:\java>

  
4 インターフェースと修飾子 
 クラスにはpublic,protected,privateといった修飾子がある。インターフェースにもこれらの修飾子を使うことが可能である。もちろん、インターフェースに含まれる変数やメソッドにも用いることも可能である。
public interface Shape2D{
     // 面積を計算する
   public double getArea( );
}
  
5 インターフェースの継承 
 クラスには継承という機能がある。インターフェースにも継承という機能がある。クラスは多重継承ができないが、インターフェースは多重継承をすることができる。その場合、extendsの後に継承したいインターフェースをカンマで区切って記述する。
public interface Interface extends Interface1,Interface2{
     // 面積を計算する
   public double method( );
}
  
6 インターフェースとinstaceof演算子
 クラスはinstanceof演算子で、インスタンスがそのクラスのインスタンスか否かを判定することができる。インターフェースも同様にインスタンスが指定されたインターフェースをインプリメントかどうかをinstanceof演算子を用いて調べることができる。
 下記の例は、面積の合計を求める先ほどのサンプルをinstanceof演算子を用いて、円の面積の合計と長方形の面積の合計をそれぞれ算出するように書き換えたものである。
Test.java

public class Test{
   public static void main(String args[ ]){
         Shape2D array[ ] = new Shape2D[5];

         array[0] = new Circle(5,3,2);
         array[1] = new Rect(5,3,20,7);
         array[2] = new Circle(3,1,1);
         array[3] = new Rect(0,0,3,3);
         array[4] = new Circle(-5,2,2);

         double areaCircle = 0;    // 円の面積
         double areaRect   = 0;    // 長方形の面積

    for( int i = 0 ; i < array.length ; i++ ){
              if( array[i] instanceof Circle )
                  areaCircle += array[i].getArea( );

              else
                  areaRect   += array[i].getArea( );
        
}

         System.out.println("円面積の合計="areaCircle);
         System.out.println("長方形面積の合計="areaRect);
         System.out.println("面積の合計="+ (areaCircle + areaRect));
     }
}

C:\java>java Test
円面積の合計=28.274333882308138
長方形面積の合計=69.0
面積の合計=97.27433388230814

C:\java>

 

12.マルチスレッド

12章 マルチスレッド
1.スレッドとは
 一つのプロセスで、同時に複数の処理をすることができる。例えば、ブラウザでインターネットからファイルを取得する仕事が動き、さらにユーザからの操作を受け付ける仕事があり・・・これらが同時に動いている。この仕事一つ一つをスレッドという。
複数スレッドが同時に処理することができるプログラムをマルチスレッドプログラムという。
2.スレッドのライフサイクル
 同時にスレッドが動くといっても実際はCPUが一つなので、一つ目のスレッドが動いたら二つ目のスレッドが動く、というように短時間の間に交互に動く。これをタイムディビジョン方式という。
実行状態
現在、実行されているスレッド
待機状態
通信の返事が返ってくるまで待つなどキーボード入力を待ったり、イベントが終了するまで待機している状態。または、何もすることがない状態。
実行可能状態

 本来は、すぐに実行になりたいが別のスレッドが実行中の場合は、一時待つ。
3.スレッドの作成 〜Threadクラス〜
 スレッドを作るには、java.langパッケージのThredクラスを継承すれば作られる。さらに、スレッドの中身はrunメソッドをオーバーライドして記述する。そして、スレッドクラスのstartメソッドを呼び起こせば、新しいスレッドが作成されrunメソッドが別スレッドとして実行される。
Test.java// 独自のスレッドクラス
class MyThread extends Thread{
public  MyThread( ){
}
// このrunメソッドがスレッド本体
public void run( ){
System.out.println(“MyThread Start!”);

// 10回500ミリ秒毎n”Hello”と表示する
    for (int i = 0 ; i < 10 ; i++ ) {
System.out println(“Hello”);
      try{
// sleepメソッドはThreadクラスの静的メソッドでnミリ秒(この場合500ミリ秒)待つ(待機状態)メソッド。
// 例外を投げる可能性があるので、キャッチしないとならない。
Thread.sleep(500);
}catch(Exception e){ }
}
}

public class Test {
public static void main (String args [ ] ) {
// 独自のスレッドクラスのインスタンスを作成
MyThread t = new MyThread(“Thread1”);

// スレッドを開始
t.start( ) ;

// 1秒毎に”Hi !”と10回表示する
for( int i = 0 ; i < 10 ; i++ ) {
System.out.println(“Hi !”) ;
try{
Thread.sleep(1000);
}catch(Exception e){ }
}
}
}

C:\java>javac Test.java

C:\java>java Test
Hi!
MyThread Start!
Hello
Hello
Hi!
Hello
Hello
Hi!
Hello
Hello
Hi!
Hello
Hello
Hi!
MyThread End!
Hi!
Hi!
Hi!
Hi!
C:\java>

4.Threadクラスのメソッド
●コンストラクタ概要
Thread( )
 新しいThreadオブジェクトを割り当てる
Thread(Runnable target )
 新しいThreadオブジェクトを割り当てる
Thread(Runnable target,String name)
 nameという名前の新しいThreadオブジェクトを割り当てる
Thread(String name)
 nameという名前の新しいThreadオブジェクトを割り当てる
●メソッド概要
String
getName( )        
 このスレッドの名前を返す 
boolean     
isAlive( )        
このスレッドが生存しているかどうかを判定する 
void          
join(long millis)  
 このスレッドが終了するのを、最高でmillisミリ秒待機する
void          
run( )                 
このスレッドが別個のRunnable実行オブジェクトを使用して作成された場合、そのRunnableオブジェクトのrunメソッドが呼び出される 
void
setName(String name)
このスレッドを、デーモンスレッドまたはユーザスレッドとしてマークする 
void
setPriority(int newPriority)
このスレッドの優先順位を変更する 
static void
sleep(long millis)
現在実行中のスレッドを指定されたミリ秒数の間、スリープ(一時的に実行を停止)させる
void
start( )
このスレッドの実行を開始 
static void
yield( )
現在実行中のスレッドオブジェクトを一時的に休止させ、ほかのスレッドが実行できるようにする 
5.スレッドの作成 〜Runnableインターフェース〜
Test.java// 独自のクラス
class MyRunnable implements Runnable{
// このrunメソッドがスレッド本体
public void run( ){
System.out.println(“MyRunnable Start!”);

// 10回500ミリ秒毎n”Hello”と表示する
    for (int i = 0 ; i < 10 ; i++ ) {
System.out println(“Hello”);
      try{
// sleepメソッドはThreadクラスの静的メソッドでnミリ秒(この場合500ミリ秒)待つ(待機状態)メソッド。
// 例外を投げる可能性があるので、キャッチしないとならない。
Thread.sleep(500);
}catch(Exception e){ }
}
}

public class Test {
public static void main (String args [ ] ) {
// 独自のスレッドクラスのインスタンスを作成
Runnable r = new MyRunnable( ):
Thread t = new Thread(r);

// スレッドを開始
t.start( ) ;

// 1秒毎に”Hi !”と10回表示する
for( int i = 0 ; i < 10 ; i++ ) {
System.out.println(“Hi !”) ;
try{
Thread.sleep(1000);
}catch(Exception e){ }
}
}
}

C:\java>javac Test.java

C:\java>java Test
Hi!
MyRunnable Start
Hello
Hello
Hi!
Hello
Hello
Hi!
Hello
Hello
Hi!
Hello
Hello
Hi!
MyRunnable End!
Hi!
Hi!
Hi!
Hi!
C:\java>

6.同期
 ロックとは、一つ目のスレッドがあるメソッドをロックすると別のスレッドはそのメソッド内に入れなくなる。そのメソッドを出るときにロックを解除することで別スレッドがそのメソッドに入ることができるようにする。
メソッドを作成するときに「Synchronized」修飾子をつける。javaではこれだけでロックをかける(同期をとる)ことができる。
Test.java// 口座クラス
class Account{
private int balance = 0;

 

 synchronized void deposit ( int amount ){
int a = balance;
    a = a + amount;
    try{
Thread.sleep((int)(Math.random( )*10));
}catch(Exception e){ }
    balance = a;
}
        int getBalance( ){
}
}

// 顧客クラス
class Customer extends Thread{
     Account account;

// コンストラクタ
  Customer(Account account){
this.account = account;
}

// 10円ずつ貯金することを1000回繰り返す
public void run( ) {
          for( int i = 0 ; i < 1000 ; i++ ){
account.deposit(10);
}
}
}

class Test{
public static void main(String args[ ]){
     // 口座を作る
Account account = new Account( );

        // 10人の顧客を作る
Customer customers[ ] = new Customer[10];

        // 10人の顧客が一つの口座に振り込み処理を
   //開始す
for( int i = 0 ; i < 10 ; i++ ){
customer[i] = new Customer(account);
customer[i].start( );

}

        // 10人のスレッドが終わるのを待つ
try{
for( int i = 0 ; < 10 ; i++ ){
customers[i].join( );
}
}catch(Exception e){
System.err.println(“Error:”+e);
}

     // 残高表示
System.out.println(“残高:”+account.getBalance()));
}
}

Test.java// 口座クラス
class Account{
private int balance = 0;

void deposit ( int amount ){
int a = balance;
    a = a + amount;
    try{
Thread.sleep((int)(Math.random( )*10));
}catch(Exception e){ }
    balance = a;
}

        int getBalance( ){
}
}

// 顧客クラス
class Customer extends Thread{
     Account account;

// コンストラクタ
  Customer(Account account){
this.account = account;
}

// 10円ずつ貯金することを1000回繰り返す
public void run( ) {
          for( int i = 0 ; i < 1000 ; i++ ){
account.deposit(10);
}
}
}

class Test{
public static void main(String args[ ]){
     // 口座を作る
Account account = new Account( );

        // 10人の顧客を作る
Customer customers[ ] = new Customer[10];

        // 10人の顧客が一つの口座に振り込み処理を
   //開始する
for( int i = 0 ; i < 10 ; i++ ){
customer[i] = new Customer(account);
customer[i].start( );

}

        // 10人のスレッドが終わるのを待つ
try{
for( int i = 0 ; < 10 ; i++ ){
customers[i].join( );
}
}catch(Exception e){
System.err.println(“Error:”+e);
}

     // 残高表示
System.out.println(“残高:”+account.getBalance()));
}
}

C:\java>javac Test.java

C:\java>java Test
残高:100000C:\java>

C:\java>javac Test.java

C:\java>java Test
残高:10000C:\java>

メソッドにsynchronized修飾子をつけて同期をとる方法ではなく、オブジェクトにロックをかけて同期をとる方法
Test.java// 口座クラス
class Account{
private int balance = 0;

void deposit ( int amount ){
int a = balance;
    a = a + amount;
    try{
Thread.sleep((int)(Math.random( )*10));
}catch(Exception e){ }
    balance = a;
}

        int getBalance( ){
}
}

// 顧客クラス
class Customer extends Thread{
     Account account;

// コンストラクタ
  Customer(Account account){
this.account = account;
}

// 10円ずつ貯金することを1000回繰り返す
public void run( ) {
          for( int i = 0 ; i < 1000 ; i++ ){
sychronized( account ){
account.deposit(10);
}
}
}
}

class Test{
public static void main(String args[ ]){
     // 口座を作る
Account account = new Account( );

        // 10人の顧客を作る
Customer customers[ ] = new Customer[10];

        // 10人の顧客が一つの口座に振り込み処理を開始する
for( int i = 0 ; i < 10 ; i++ ){
customer[i] = new Customer(account);
customer[i].start( );

}

        // 10人のスレッドが終わるのを待つ
try{
for( int i = 0 ; < 10 ; i++ ){
customers[i].join( );
}
}catch(Exception e){
System.err.println(“Error:”+e);
}

     // 残高表示
System.out.println(“残高:”+account.getBalance()));
}
}

C:\java>javac Test.javaC:\java>java Test
残高:100000

C:\java>

 この方法は、accountにロックをかけている。一つ目のスレッドがaccountにロックをかけると、別のスレッドはロックが解除されるまで、synchronizedで囲まれたブロック(synchronizedブロック)には、入ることができない。
7.デットロック
 デットロックとは、マルチスレッドプログラミングで起こりやすいバグの一つ。一つ目のスレッドがXというオブジェクトのロックが解除されるのを待つ。二つ目のスレッドはYというオブジェクトをロックしており、Xというオブジェクトの解放を待つ。このような場合、両方のスレッドはオブジェクトの解放を永遠に待ち続けてしまうため、プログラムは進まなくなり、いわゆる暴走の状態に陥る。このように、複数のスレッドがお互いにロックの解放を永久に待ち続けてしまうことをデットロックという。
Test.java class X{
  int x;
}

class Y{
int y;
}

class A extends Thread{
X x;
Y y;

A(X x, Y y){
this.x = x;
this.y = y;
}

void func1( ){
     System.out.println(“func1:start”);
synchronized(x){
synchronized(y){
try{
sleep(10);
}catch(Exception e){ }
}
}
}

void func2( ) {
System.out println(“func2:start”);
synchronized(y){
synchronized(x){
try{
sleep(10);
}catch(Exception e){ }
}
}
}

public void run( ){
for ( int i = 0 ; i < 1000 ; i++ ){
func1( );
func2( );
}
}
}

class Test{
pubic static void main ( String args[ ]) {
X x = new X( );
Y y = new Y( );

A a[ ] = new A[10];
   for( int i = 0 ; < 10 ; i++ ) {
try{
                 a[i].join( );
}catch(Exception e){ }
}
}
}

C:\java>javac Test.javaC:\java>java Test
func1:start
func1:start
func1:start
func1:start
func1:start
func1:start
func1:start
func1:start
func2:start
func1:start
func1:start
func2:start       ←ここでデットロックが発生

 

 多くの場合、ロックするオブジェクトを同じ順番でロックすれば、デットロックは防げる。例えば上記では、func2のsychronizedでロックする順番をfunc1と同じように「x→y」の順番でロックすればデットロックは防げる。言い換えるとオブジェクトに優先順位を与えるということになる。
このようにマルチスレッド特有のバグが発生した場合の多くは、バグを再現させることすら難しく、デバッグ作業は大変な作業になってしまう。バグが起きないようにあらかじめちゃんと設計してからプログラミングする必要がある。
8.スレッド間の通信
 スレッドから一時的にロックを解放し、他のスレッドにsynchronizedメソッドもしくはsynchronizedブロックを実行するチャンスを与えることができる。例えば、一つ目のスレッドはメッセージを受信するスレッドで、二つ目のスレッドはメッセージを与えるスレッドである。メッセージ受信のスレッドは、メッセージボックスにメッセージがなければメッセージが格納されるまですることがない。このような場合ほかのスレッド(メッセージ送信スレッド)に制御を移す。逆にメッセージ送信スレッドは、メッセージボックスがいっぱいならば、メッセージ受信スレッドがメッセージを処理し終わるまで待たなければならない。
このように、他のスレッドが処理し終わるまで待つ場合は「wait」メソッドを使用し、他のスレッドに制御を与える場合は「notify」または「notifyAll」メソッドを使用する。notifyAllメソッドはwaitしているすべてのメソッドに対して通知をするが、notifyメソッドは、いずれか一つのスレッドに対してのみ通知する。どのスレッドに通知されるかは、JVMが勝手に決めていいことになっている。
Test.java import java.util.*;

// メッセージボックス
class MessageBox{
     private String message;

// メッセージボックスにメッセージを入れる
synchronized void  messageIn(String msg){
try{
// メッセージがすでに入っていれば、なくなるまで待つ
while( message != null){
wait( );
}

// メッセージを入れる
message = msg;

// メッセージを入れ終わったら別のスレッドを起こす
notifyAll( );

// 以下例外処理
}catch(Exception e){
System.err.println(“messageIn:Error:”+e);
System.exit(1);
}
}

// メッセージ取り出し
synchronized String messageOut( ){
try {
// メッセージがなければ、メッセージが入るまで待つ
while(message == null){
wait( );
}

// メッセージ取り出し
String s == message;
message = null ;

// 取り出したら別のスレッドを起こす
notifyAll( );
return s ;
// 以下例外処理
} catch (Exception e ){ }
System.err.println(“messageOut:Error”);
System.exit( 1);
return “”;
}
}

// 以下例外処理
class MessageSender extends Thread{
public void run( ){
for( int i = 0 ; i < 100 ; i++ ){
Test.msgBox.messageIn(“”+Math.random( ));
          }
}
}

// メッセージを取り出し、表示するスレッド
class MessageReciever extends Thread {
public void run ( ){
          for ( int i = 0 ; i < 100 ; i++ ){
System.out.println(Test.msgBox.messageOut( ));
}
}
}

class Test{
public static MessageBox msgBox = new MessageBox( );

public static  void main(String args[ ]){
MessageReciever reciever = new MessageReciever( );
reciever.start( );

MessageSender sender = new MessageSender( );
sender.start( );

try{
sender.join( ;
reciever.join( );
}catch(Exception e){ }
}
}

C:\java>javac Test.javaC:\java>java Test
0.2894081823761079
0.20714542091971344

中略

0.519766868805261
0.6181891980702379

C:\java>

  

REVENGE

 5月7日(土)、GWの終わりに草野球の練習試合があった。
 チームを立ち上げ、草野球の活動としては2回目、前回の開幕戦のリベンジマッチだった。場所は、前回と同じく萩山公園。心配された雨も天の恵み晴れ男マサチャンの参加で雨上がりが気持ちいい絶好の野球日和となった。(ちょっと泥んこ野球だったけどそれもまた良し)管理もしっかり行き届いていてきれいなグラウンドだ。ぜひ、また使いたい。
 
●試合結果
                       計
 介護チーム 0 0 1 0 0 3   4
 我 チーム  1 0 3 0 0 1×  5
 
 tomo、ホリ、さっつ、ホリ ― さっつ、tomo
 二塁打 ナオヤンキー 本塁打 tomo
 MVP マサチャン(サヨナラヒット)

●戦評
 初回、tomoのセンター前ヒットを皮切りにさっつのライト前で先制。その後、介護チームに連打され一時同点に。がすぐさま裏の攻撃でtomoの2ランで逆転!ピッチャーをホーリーに変え、抜群の制球力で相手の攻撃を抑えた。最終回に味方のエラーで同点にされるも、満塁でマサチャンのレフト前ヒットで劇的にサヨナラ勝ち。
 マサチャンの加入で、チームのムードは最高潮に。攻撃面でも、先発全員安打、つながりのあるいい打線であった。守備面では、tomo、ホーリー共に無四球。守りのリズムもまずまずであった。

●課題
 相手チームのレベルに合わせないで、相手のスキをつき貪欲に次の塁を狙って大量得点を目指す。

●試合後は、またもや小金井公園で今度は花見ではなく、葉っぱ見をした。みっちゃんの誕生日だったので反省会&誕生日会!しかし相変わらず、ナオヤンキーは熱い!とにかくHot.