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”と表示する public class Test { // スレッドを開始 // 1秒毎に”Hi !”と10回表示する |
|
C:\java>javac Test.java
C:\java>java Test |
|
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”と表示する public class Test { // スレッドを開始 // 1秒毎に”Hi !”と10回表示する |
|
C:\java>javac Test.java
C:\java>java Test |
|
6.同期 | |
ロックとは、一つ目のスレッドがあるメソッドをロックすると別のスレッドはそのメソッド内に入れなくなる。そのメソッドを出るときにロックを解除することで別スレッドがそのメソッドに入ることができるようにする。 メソッドを作成するときに「Synchronized」修飾子をつける。javaではこれだけでロックをかける(同期をとる)ことができる。 |
|
Test.java// 口座クラス class Account{ private int balance = 0;
synchronized void deposit ( int amount ){ // 顧客クラス // コンストラクタ // 10円ずつ貯金することを1000回繰り返す class Test{ // 10人の顧客を作る // 10人の顧客が一つの口座に振り込み処理を } // 10人のスレッドが終わるのを待つ // 残高表示 |
Test.java// 口座クラス class Account{ private int balance = 0; void deposit ( int amount ){ int getBalance( ){ // 顧客クラス // コンストラクタ // 10円ずつ貯金することを1000回繰り返す class Test{ // 10人の顧客を作る // 10人の顧客が一つの口座に振り込み処理を } // 10人のスレッドが終わるのを待つ // 残高表示 |
C:\java>javac Test.java
C:\java>java Test |
C:\java>javac Test.java
C:\java>java Test |
メソッドにsynchronized修飾子をつけて同期をとる方法ではなく、オブジェクトにロックをかけて同期をとる方法 | |
Test.java// 口座クラス class Account{ private int balance = 0; void deposit ( int amount ){ int getBalance( ){ // 顧客クラス // コンストラクタ // 10円ずつ貯金することを1000回繰り返す class Test{ // 10人の顧客を作る // 10人の顧客が一つの口座に振り込み処理を開始する } // 10人のスレッドが終わるのを待つ // 残高表示 |
|
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{ class A extends Thread{ A(X x, Y y){ void func1( ){ void func2( ) { public void run( ){ class Test{ A a[ ] = new A[10]; |
|
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 Test{ public static void main(String args[ ]){ MessageSender sender = new MessageSender( ); try{ |
|
C:\java>javac Test.javaC:\java>java Test 0.2894081823761079 0.20714542091971344 中略 0.519766868805261 C:\java> |
|
Category: Java
14-22.Calendar
22.Calendar |
Calendarは、DateオブジェクトとYEAR、MONTH、DAY、HOURなどの整数フィールドの間で変換を行うための抽象クラス。実際には具象クラスであるGregorianCalendarを使用する。 |
import java.util.Calendar |
コンストラクタ |
Date() 現在の日付と時刻で初期化されたDateインスタンスを作成。 |
Date(long msec) グリニッジ標準時1970年1月1日0時からmsecミリ秒経過したDateインスタンスを作成。 |
メンバー変数 | |
static int | YEAR getおよびsetのための、年を示すフィールド値。 |
static int | MONTH getおよびsetのための、月を示すフィールド値。 |
static int | DATE getおよびsetのための、日を示すフィールド値。 |
static int | DAY_OF_MONTH getおよびsetのための、日を示すフィールド値。 |
static int | DAY_OF_WEEK getおよびsetのための、曜日を示すフィールド値。 |
static int | DAY_OF_YEAR getおよびsetのための、1/1から数えて何日目かを示す。 |
static int | WEEK_OF_MONTH getおよびsetのためのフィールド値で、現在の月の何週目かを示す。 |
static int | WEEK_OF_YEAR getおよびsetのためのフィールド値で、現在の年の何週目かを示す。 |
static int | DAY getおよびsetのための、日を示すフィールド値。 |
static int | HOUR getおよびsetのための、何時かを示す。(最大は12) |
static int | HOUR_OF_DAY getおよびsetのための、何時かを示す。(最大は24) |
static int | MINUTE getおよびsetのための、分を示すフィールド値。 |
static int | SECOND getおよびsetのための、秒を示すフィールド値。 |
static int | MILISECOND getおよびsetのための、ミリ秒を示すフィールド値。(Windowsでは10ミリ秒単位) |
static int | AM 午前を表す定数 |
static int | PM 午前を表す定数 |
static int | AM_PM 午前を表す定数 |
static int | SUNDAY 日曜日を示すDAY_OF_WEEKフィールドの値。 |
static int | MONDAY 月曜日を示すDAY_OF_WEEKフィールドの値。 |
static int | TUESDAY 火曜日を示すDAY_OF_WEEKフィールドの値。 |
static int | WEDNESDAY 水曜日を示すDAY_OF_WEEKフィールドの値。 |
static int | THURSDAY 木曜日を示すDAY_OF_WEEKフィールドの値。 |
static int | FRIDAY 金曜日を示すDAY_OF_WEEKフィールドの値。 |
static int | SATURDAY 土曜日を示すDAY_OF_WEEKフィールドの値。 |
static int | JANUARY 1月を示すMONTHフィールドの値。 実際の値は0となっています。 |
static int | FEBRUARY 2月を示すMONTHフィールドの値。 実際の値は1となっている。 |
static int | MARCH 3月を示すMONTHフィールドの値。 実際の値は2となっている。 |
static int | APRIL 4月を示すMONTHフィールドの値。 実際の値は3となっている。 |
static int | MAY 5月を示すMONTHフィールドの値。 実際の値は4となっている。 |
static int | JUNE 6月を示すMONTHフィールドの値。 実際の値は5となっている。 |
static int | JULY 7月を示すMONTHフィールドの値。 実際の値は6となっている。 |
static int | AUGUST 8月を示すMONTHフィールドの値。 実際の値は7となっている。 |
static int | SEPTEMBER 9月を示すMONTHフィールドの値。 実際の値は8となっている。 |
static int | OCTORBER 10月を示すMONTHフィールドの値。 実際の値は9となっている。 |
static int | NOVEMBER 11月を示すMONTHフィールドの値。 実際の値は10となっている。 |
static int | DECEMBER 12月を示すMONTHフィールドの値。 実際の値は11となっている。 |
static int | UNDECIMBER 13月を示すMONTHフィールドの値。 実際の値は12となっている。 (太陽暦では使用しない、太陰暦で使用。) |
static int | DST_OFFSET getおよびsetのためのフィールド値で、夏時間のオフセットをミリ秒単位で示す。 |
static int | ERA getおよびsetのためのフィールド値で、ユリウス暦のADまたはBCなどの年代を示す。 |
コンストラクタ |
Calendar() デフォルトのタイムゾーンおよびロケールを使用してCalendarインスタンスを作成。 |
メソッド | |
Calendar | getInstance() デフォルトのタイムゾーンおよびロケールを使用してCalendarインスタンスを作成。 |
Date | getTime() インスタンスの持つの時刻を返す。 |
int | get(int field) fieldで示されるフィールドの値を返します。 例えば、Calendarインスタンスcalが示す時間は24時間制で何時になるかは cal.get(Calendar.HOUR_OF_DAY) とする。 |
long | getTimeInMillis() 現在のインスタンスを1970年1月1日0時0分0秒からのミリ秒で取得する。 |
void | set(int field, int value) filedで示されるフィールド値を設定します。 例えば、Calendarインスタンスcalが示す時間は24時間制で13時にする場合は cal.set(Calendar.HOUR_OF_DAY, 13) とする。 |
void | set(int year, int month, int day) year年month月day日に設定します。時間などその他のフィールドは保持される。 例えば、2004年8月31日に設定する場合は、 cal.set(2004,Calendar.AUGUST, 31) または ca.set(2004, 8-1, 31) とする。 |
void | set(int year, int month, int day, int hour, int minute) year年month月day日hour時minute分に設定する。秒などその他のフィールドは保持される。 例えば、2004年8月31日10時30分に設定する場合は、 cal.set(2004,Calendar.AUGUST, 31, 10, 30) または ca.set(2004, 8-1, 31, 10, 30) とする。 |
void | set(int year, int month, int day, int hour, int minute, int second) year年month月day日hour時minute分second秒に設定します。その他のフィールドは保持される。 例えば、2004年8月31日10時30分10秒に設定する場合は、 cal.set(2004,Calendar.AUGUST, 31, 10, 30, 10) または ca.set(2004, 8-1, 31, 10, 30, 10) とする。 |
void | setTime(Date time) 時刻timeを設定。 |
void | setTimeInMillis(long l) 現在のインスタンスを1970年1月1日0時0分0秒からのミリ秒で設定。 |
void | clear() すべてのフィールドがクリアされます。日時は1970年1月1日0時0分0秒になる。 |
void | clear(int field) fieldで示されるフィールドがクリアされる。 |
void | add(int field, int amount) fieldをamoutだけ加算します。 現在のCalndarインスタンスcalの30日前を求めるには cal.add(Calendar.DATE, -30) とする。 |
void | roll(int field, boolean up) upがtrueの場合はfieldで示されるフィールドを1だけ加算。 upがfalseの場合はfieldで示されるフィールドを1だけ減算。 ただし、上のフィールドは変更しません。 例えば、8月31日の場合、DATEフィールドをupさせた場合は、8月1日となる。 |
boolean | equals(Object obj) objがCalendarクラスもしくはそのサブクラスで、同じ値を持つ場合はtrueを、それ以外はfalseを返す。 |
boolean | after(Object obj) objがCalendarクラスもしくはそのサブクラスで、自分自身の方が後ならばtrueを返す。 |
boolean | before(Object obj) objがCalendarクラスもしくはそのサブクラスで、自分自身の方が前ならばtrueを返す。 |
int | getFirstDayOfWeek() 週の最初が何曜日かを返す。 例えば日本やアメリカではSUNDAY(1)が、フランスではMONDAY(2)が返される。 |
int | getMinimum(int field) fieldで示されたフィールドの最小値を示す。 たとえば、グレゴリオ暦のDAY_OF_MONTHは1。 |
int | getMaximum(int field) fieldで示されたフィールドの最大値示します。 たとえば、グレゴリオ暦のDAY_OF_MONTHは31。 |
int | getGreatestMinimum(int field) fieldで示されたフィールドの最小値が変化する場合、その最大値を取得する。 たとえば、グレゴリオ暦のDAY_OF_MONTHは1。 |
int | getLeastMaximum(int field) fieldで示されたフィールドの最大値が変化する場合、その最小値を取得する。 たとえば、グレゴリオ暦のDAY_OF_MONTHは28。 |
String | toString() 現在のインスタンスを文字列に変換します。 コンパイラなどによって異なるのでデバッグ以外には使用しない。 |
GregorianCalendar | |
import java.util.GregorianCalendar |
|
GregorianCalendarは、Calendarクラスの具象クラス。日本を含め、世界のほとんどの国で採用されている太陽暦のカレンダー。ほとんどのメソッドは、Calendarクラスから受け継がれたものを利用。 | |
GregorianCalendarのメンバー変数 | |
int | AD キリスト紀元 (西暦) を示す ERA フィールドの値。 |
int | BC キリスト紀元 (BC) より前の期間を示す ERA フィールドの値。 |
GregorianCalendarのコンストラクタ | |
GregorianCalendar() デフォルトのタイムゾーンおよびロケールを使用してGregorianCalendarインスタンスを作成。 |
|
GregorianCalendar(int year, int month, int date) デフォルトのタイムゾーンおよびロケールを使用して、指定した年月日でGregorianCalendarインスタンスを作成。 |
|
GregorianCalendar(int year, int month, int date, int hour, int minute) デフォルトのタイムゾーンおよびロケールを使用して、指定した日時でGregorianCalendarインスタンスを作成。 |
|
GregorianCalendar(int year, int month, int date, int hour, int minute, int second) デフォルトのタイムゾーンおよびロケールを使用して、指定した日時でGregorianCalendarインスタンスを作成。 |
|
GregorianCalendarのメソッド | |
boolean | isLeapYear(int year) year年が閏年ならtrueを返す。 |
14-21.Date
21.Date | |
Dateクラスは日付と時間に関する情報を扱う。 | |
import java.util.Date |
|
コンストラクタ | |
Date() 現在の日付と時刻で初期化されたDateインスタンスを作成。 |
|
Date(long msec) グリニッジ標準時1970年1月1日0時からmsecミリ秒経過したDateインスタンスを作成。 |
|
メソッド | |
boolean | after(Date d) dがこのオブジェクトよりも後にある場合にtrueを返す。 |
boolean | before(Date d) dがこのオブジェクトよりも前にある場合にtrueを返す。 |
static boolean |
equals(Date d) dがこのオブジェクトと等しい場合にtrueを返します。 |
static boolean |
isDigit(char ch) chが数字かを調べます。定義されていればtrueが返される。 全角半角は問わない。 |
long | getTime() このオブジェクトがグリニッジ標準時1970年1月1日0時からどのくらい経過しているかを返す。単位はミリ秒です。 |
void | setTime(long msec) グリニッジ標準時1970年1月1日0時からmsecミリ秒経過した日付と時間に設定する。 |
String | toString() 日付を文字列に変換して返す。 |