- PR -

ThreadPoolExecutorについて

投稿者投稿内容
WT
常連さん
会議室デビュー日: 2004/07/22
投稿数: 29
投稿日時: 2007-12-13 02:03
ThreadPoolExecutorについて質問させてください。
JDK1.5.0_10
を使ってマルチスレッド処理を含むプログラムを作成する必要があり、ThreadPoolExecutorを使っています。
プログラミングの過程で(自分が)発見したThreadPoolExecutorの仕様(動き)について間違ってないか教えてください。

1)ThreadPoolExecutorは自分自身でThreadオブジェクト(RunnableやCallableオブジェクト)を作るわけでなく、(どちらかと言うと)Threadオブジェクトをworker(Queue)に溜め込んで順序良く実行していく為の機構である。

2)上記のようにThreadPoolExecutorは自分自身でTHreadオブジェクトを作成する訳ではないので、一度使ったThreadオブジェクトを再利用するわけではない。

3)ThreadPoolExecutorによって実行されたThreadオブジェクトは処理が完了した際、ThreadPoolExecutorが削除するわけではない。


ちょっと3)の表現が自信ないのですが・・・
上記三つの認識は正しいでしょうか?
nagise
ぬし
会議室デビュー日: 2006/05/19
投稿数: 1141
投稿日時: 2007-12-13 02:25
私の理解ではThreadPoolExecutorは内部でプールしているThreadを使って
Executor#execute()で渡されるRunnableおよび、
ExecutorService#submit()で渡されるCallableを実行する、
名前のとおりThreadをプールするExecutorと解しています。

そのように解する根拠はjavadoc
引用:

プールされた複数のスレッドの 1 つを使用して送信された各タスクを実行する ExecutorService です。


と記述されている点に因ります。

ThreadPoolExecutorがその名に反し、またjavadocの記述に反し
Threadをプールしないと考える根拠は何でしょうか?
かつのり
ぬし
会議室デビュー日: 2004/03/18
投稿数: 2015
お住まい・勤務地: 札幌
投稿日時: 2007-12-13 03:11
スレッドはExecutorServiceが内部で保持している、
ThreadFactoryのインスタンスで生成されます。
わたなべ
大ベテラン
会議室デビュー日: 2007/12/09
投稿数: 123
お住まい・勤務地: 札幌
投稿日時: 2007-12-13 10:28
Executorを理解するポイントは、並行処理における「処理を実行するオブジェクト」と「実行する処理」を分離するという事です。
処理を実行するオブジェクトはThreadですが、これはExecutorの中で管理されます。
開発者は「実行する処理」のみを作成し、【どんな風に】実行するかをExecutorに支持するだけです。

1)ThreadPoolExecutorは自分自身でThreadオブジェクト(RunnableやCallableオブジェクト)を作るわけでなく、(どちらかと言うと)Threadオブジェクトをworker(Queue)に溜め込んで順序良く実行していく為の機構である。

NO.
溜め込むのはTherdオブジェクトではなく、Callable(Runnabel)インターフェイスを実装したクラスのインスタンスだけです。
プールの容量を超えない限りは、順序良く実行するわけではなく並行(同時)に実行します。
プールの容量を超えた場合、どんな順序で実行するかはパラメータで指定できます。
例えば、FIFOだったりLIFOだったり、独自の優先度を定義したり。


2)上記のようにThreadPoolExecutorは自分自身でTHreadオブジェクトを作成する訳ではないので、一度使ったThreadオブジェクトを再利用するわけではない。
NO.
1)でも書きましたが、ThreadはPoolされ、再利用されます。
Threadの生成コストが高いため、実行する部分だけを作成するという設計になってます。

3)ThreadPoolExecutorによって実行されたThreadオブジェクトは処理が完了した際、ThreadPoolExecutorが削除するわけではない。
YES.
Executorのshutdownをしない限りは、Threadオブジェクトは削除されません。
(実行されているわけではなく、待機している状態)

ranco
大ベテラン
会議室デビュー日: 2007/11/02
投稿数: 112
投稿日時: 2007-12-13 11:16
すこし補足します:
corePoolSizeを超えるぶんのスレッドは、keepAliveTime以上アイドルだと終わらされます(ガベコレの対象になる)。
WT
常連さん
会議室デビュー日: 2004/07/22
投稿数: 29
投稿日時: 2007-12-13 12:56
皆様、アドバイスありがとうございます。
下記に整理しました。

1)ThreadPoolExecutorは自分自身でThreadオブジェクト(RunnableやCallableオブジェクト)を作るわけでなく、(どちらかと言うと)Threadオブジェクトをworker(Queue)に溜め込んで順序良く実行していく為の機構である。

皆様の見解:No
私の考え:Yes

「順序良く実行していく」には表現が悪かったと反省してます(不足していますね・・・)
私の考えを書かせてもらいます。
ThreadPoolExecutorを生成する側(呼び出し元)のクラスでThreadPool.submit()を呼び出した順番にスレッドが動き出す。動き出した後のスレッドは並行処理(同時処理)される。
「順序良く実行して行く」と書いたのは「submit()が呼び出された順番にスレッドが割り当てられていくんですよね?」と聞きたかったのです。



2)上記のようにThreadPoolExecutorは自分自身でTHreadオブジェクトを作成する訳ではないので、一度使ったThreadオブジェクトを再利用するわけではない。

皆様の見解:No
私の考え:Yes


ここが勘違いしていた最大のポイントなのかも知れないのですが・・・
この投稿を行う前にゴミプログラムを作成し、調査していました。
下記のようなゴミプロです。

----------------------------
public class ThreadTest {

ThreadPoolExecutor executor = null;

public ThreadTest(){
int threadNum = 2;
int queuInitialSize = 5;
executor = new ThreadPoolExecutor(threadNum, threadNum,
5000L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(queuInitialSize));
}

public void execute(){

for(int i=0;i<7;i++){
Task task = new Task();

try{
executor.submit(task);
}catch(Exception e){
e.printStackTrace();
}

}
}

public class Task implements Callable {

String name;

public Task(){
name = Integer.toString(i);
}

public String call() {
System.out.println("ThreadName=[" + name + "]");
System.out.println("ThreadAddress=[" + this.hashCode() + "]");
return this.toString();
}

}

public static void main(String[] args) {

ThreadTest tester = new ThreadTest();
tester.execute();
System.exit(0);

}

}
------------------------------------


スレッドが再利用されているか、Object.hashCode()を使って確認しようとしたのです。
(上記コードで言えば36行目のSystem.out.println()で出力される表示で確認しようとしていました)

「Object.hashCode()はそのオブジェクトの先頭アドレスを返す」事を前提として考えています。(オーバーライドされていれば別ですが)
表示されるhashCodeは、毎回異なっていました。
なので「Threadオブジェクトの先頭アドレスが毎回異なる=毎回、違うスレッドが使用されている」と考えた次第です。

しかし、皆さんのアドバイスを見るに、「上記プログラムはCallableオブジェクトの先頭アドレスを表示しているに過ぎない」のかな?と、考えました。
(インターフェイスであるCallableの事をCallableオブジェクトと呼ぶのも間違っている気がしますが・・・)


3)ThreadPoolExecutorによって実行されたThreadオブジェクトは処理が完了した際、ThreadPoolExecutorが削除するわけではない。

皆様の見解:Yes
私の考え:Yes

ココは認識通りでした。ただ、Executorのshutdownをしない限りは、Threadオブジェクトは削除されないのですが、Callableオブジェクトは削除できるのでしょうか?
・・・と、新たな疑問がわいてきました。



以上、長文で申し訳ありません。
nagise
ぬし
会議室デビュー日: 2006/05/19
投稿数: 1141
投稿日時: 2007-12-13 12:58
Executor#execute()に渡すのがThreadではなくRunnableというところがポイントですね。
execute()に渡したRunnableはnew Thread(runnable)でThreadを作って実行されるとは限りません
ThreadPoolExecutorならプールされたThreadの内部で渡したRunnableのrun()を
そのまま呼び出していることでしょう。

落ち着いてThreadとRunnableの違いを確認してみてください。
execute()ではRunnableを渡すのに
「ThreadPoolExecutorは自分自身でThreadオブジェクトを作るわけでない」
のだとしたら、どうやって並列に実行するのでしょう?
そのThreadはどこからやってくるのでしょう?外から渡されていないでしょう?

Runnable ≠ Threadであることに注意すれば理解の助けになるのではないでしょうか。
nagise
ぬし
会議室デビュー日: 2006/05/19
投稿数: 1141
投稿日時: 2007-12-13 13:01
おお。書いている間に返答が。
話が前後している部分は読み飛ばしてください。

「Callableオブジェクトは削除」とはどういうことを意図していますか?
ガーベッジコレクションにより、該当のCallableへの参照がなくなれば
自動的に削除されますし、参照が存在する間は削除されないのがJavaのメモリ管理です。
C言語のfreeをイメージされているのでしょうか?

スキルアップ/キャリアアップ(JOB@IT)