Solaris JVM では、任意のテキストエディタと make(1S)、さらに次の構成要素を使用して、Java プログラミングを行うことができます。
表 3-1 Java プログラミング環境の構成要素javac |
Java コンパイラ。Java ソースコードのファイル (name.java) を、Java インタプリタ java(1) が処理できるバイトコードのファイル (name.class) に変換します。Java アプリケーションと Java アプレットの両方がコンパイルされます。 |
javald |
ラッパージェネレータ。Java アプリケーションのコンパイルと実行に必要な環境情報を収集するラッパーを作成します。ラッパーが呼び出されるまで、指定されたパスが結合されないため、JAVA_HOME と CLASSPATH の再配置が可能です。 |
java |
Java インタプリタ。コマンドとして呼び出され Java アプリケーションを実行したり、HTML コードを介してブラウザから呼び出されてアプレットを実行したりすることができます。 |
appletviewer |
Java アプレットビューア。指定されたドキュメントや資源を表示したり、ドキュメントによって参照されるアプレットを実行したりします。 |
javap |
Java クラスファイル逆アセンブラ。javac によってコンパイルされたバイトコードのクラスファイルを逆アセンブルし、その結果を stdout に出力します。 |
make(1S) については、『プログラミングユーティリティ』の「make ユーティリティ」の章を参照してください。
一般的な Java 環境変数は次のとおりです。
表 3-2 Java 環境変数JAVA_HOME |
Java ソフトウェアのベースディレクトリのパス。たとえば、$JAVA_HOME/bin には、javac、 java、 appletviewer、 javap、 javah がすべて格納されています。Solaris JVM を使用するために設定する必要はありません。 |
CLASSPATH |
コンパイルによって生成された *.class ファイルを含むディレクトリへのパスをコロン (:) で区切ったリスト。 javac、 java、 javap、 javah によって使用されます。この環境変数が設定されていない場合、すべての Solaris JVM 実行ファイルは /usr/java/lib/classes.zip をデフォルトのクラスパスとみなします。Solaris JVM を使用するために設定する必要はありません。 |
PATH |
通常の実行ファイル検索パスのリスト。$JAVA_HOME/bin を含めることができます。 |
JVM ツールは /usr/java/bin に格納され、各実行ファイルへのシンボリックリンクは /usr/bin に格納されます。このため、新たにインストールした JVM パッケージを使用するために、ユーザーの PATH 変数にパスを追加する必要はありません。また、すべての Solaris JVM 実行ファイルは、標準の Java クラスライブラリを検索するデフォルトのパスを、/usr/java/lib/classes.zip とみなします。
基本 Java プログラミング環境には、デバッガはありません。Java プログラム用のデバッガは、Solaris には同梱されていない Java WorkShopTM に含まれています。
Java プログラムは、アプリケーションとアプレットの 2 つの形態に分類されます。
Java アプリケーションは、コマンド行から Java インタプリタを起動し、コンパイル済みのアプリケーションを含むファイルを指定することによって実行されます。
Java アプレットは、ブラウザから実行されます。ブラウザが解釈する HTML コードには、コンパイル済みのアプレットを含むファイル名を指定します。ブラウザはそのアプレットを読み込み実行するために、Java インタプリタを呼び出します。
例 3-1 は、stdout に「Hello World」と表示するアプリケーションのソースです。メソッドは、呼び出し時に引数を受け取りますが、引数に対して処理は行いません。
// // HelloWorld アプリケーション // class HelloWorldApp{ public static void main (String args[]) { System.out.println ("Hello World"); } } |
C と同様に、Java では最初に呼び出すメソッドまたは関数が main と認識されることに注意してください。キーワード public は、そのメソッドが誰でも実行できるようにします。static は、main が HelloWorldApp クラスを参照するようにします。つまり、static はこのクラスのインスタンスに 1 つだけ存在します。void は、main が何も返さないことを示します。args[] は、String 型の配列を宣言しています。
このアプリケーションをコンパイルするには、次のようにコマンドを実行します。
$ javac HelloWorldApp.java |
アプリケーション実行するには、次のようにコマンドを実行します。
$ java HelloWorldApp arg1 arg2 ... |
例 3-2 は、例 3-1 のアプリケーションと同じように動作するアプレットのソース例です。
// // HelloWorld アプレット // import java.awt.Graphics; import java.applet.Applet; public class HelloWorld extends Applet { public void paint (Graphics g) { g.drawString ("Hello World", 25, 25); } } |
アプレットでは、参照先クラスを必ず明示的にインポートする必要があります。キーワード public と void は、アプリケーションの場合とそれぞれ同じ意味を持ちます。extends は、HelloWorld クラスが Applet クラスからの継承であることを示します。
このアプレットをコンパイルするには、次のようにコマンドを実行します。
$ javac HelloWorld.java |
アプレットは、HTML コードを使用してブラウザ中に呼び出されます。アプレットを実行するのに最小限必要な HTML ページの構成は次のとおりです。
<title>Test</title> <hr> <applet code="HelloWorld.class" width=100 height=50> </applet> <hr> |
多くの場合、Java アプリケーションが正しく実行されるかどうかは、JAVA_HOME、CLASSPATH、LD_LIBRARY_PATH 環境変数の設定に影響されます。これらの環境変数は、ユーザーが任意のパスを設定できるため、適切でないディレクトリがパス中に設定されている可能性もあります。一般的に、アプリケーションは CLASSPATH 変数に固有の値を必要とします。
javald(1) は、Java アプリケーションのラッパーを生成するコマンドです。ラッパーは、JAVA_HOME、CLASSPATH、LD_LIBRARY_PATH 環境変数のいずれかあるいはすべてに、正しいパスを設定できます。ただし、この設定がこれらの環境変数のユーザー設定値に影響することはありません。Java アプリケーションの実行中は、ラッパーによる設定が、ユーザーの設定よりも優先されます。ラッパーは、Java アプリケーションが実際に実行されるまで指定されたパスを割り当てないので、アプリケーションを再配置しやすくなります。
通常は、ネイティブメソッドを使用して、thr_setconcurrency(3T) などの Solaris 固有の機能にアクセスしないようにしてください。アプリケーションが Solaris 用のみに限定され、100% Pure Java に準拠しなくなってしまうためです。
Java アプリケーションで thr_setconcurrency (3T)が必要になる場合はほとんどありません。必要になる場合としては、たとえば、ダミースレッドを使用して実行を繰り返すデモアプリケーション、あるいは行列の乗算や並列化されたグラフィックス計算などの演算アプリケーションなどが考えられます。
並列化された行列の乗算などの演算アプリケーションでは、ネイティブメソッドを使用して、thr_setconcurrency(3T) を呼び出す必要があります。これは、Java アプリケーションから同時に使用可能な資源を十分に使用して、複数のプロセッサを活用できるようにするためです。この方法は、大部分の Java アプリケーションやアプレットには必要ありません。次に、ネイティブメソッドを使用して thr_setconcurrency(3T) を呼び出す例を示します。
1 つ目の要素は、MPtest_NativeTSetconc() を使用する Java アプリケーション、MPtest.java です。このアプリケーションは、1 行ずつ識別情報を表示し、10,000,000 回ループして演算処理をシミュレートするスレッドを 10 個作成します。
import java.applet.*; import java.io.PrintStream; import java.io.*; import java.net.*; class MPtest { static native void NativeTSetconc(); static public int THREAD_COUNT = 10; public static void main (String args[]) { int i; // sysconf (_SC_NPROCESSORS_ONLN) に // Solaris での並列処理を設定 NativeTSetconc(); // スレッドを開始 client_thread clients[] = new client_thread[ THREAD_COUNT ]; for ( i = 0; i < THREAD_COUNT; ++i ){ clients[i] = new client_thread(i, System.out); clients[i].start(); } } static { System.loadLibrary("NativeThreads"); } class client_thread extends Thread { PrintStream out; public int LOOP_COUNT = 10000000; client_thread(int num, PrintStream out){ super( "Client Thread" + Integer.toString( num ) ); this.out = out; out.println("Thread " + num); } public void run () { for( int i = 0; i < this.LOOP_COUNT ; ++i ) {; } } } |
2 つ目の要素は、javah(1) ユーティリティを使用して、MPtest.java から作成した C スタブファイルの MPtest.c です。このファイルを作成するには、次のようにコマンドを実行します。
% javac MPtest.java % javah -stubs MPtest |
3 つ目の要素は、同じく javah(1) ユーティリティを使用して、MPtest.java から作成した C ヘッダーファイルの MPtest.h です。
% javah MPtest |
4 つ目の要素は、C ライブラリインタフェースへの呼び出しを行う C 関数の NativeThreads.c です。
#include <thread.h> #include <unistd.h> #include <jni.h> JNIEXPORT void JNICALL Java_MPtest_NativeTSetconc(JNIEnv *env, jclass obj) { thr_setconcurrency(sysconf(_SC_NPROCESSORS_ONLN)); } |
次のようなメークファイルを使用すると、4 つのファイルから、MPtest.class という Java アプリケーションを簡単に作成することができます。
# make は次の処理を行います。 # 1. make MPtest を 実行 # 2. thr_setconcurrency(_SC_NPROCESSORS_ONLN) へのネイティブ呼び出しを # 取り込むために NativeThreads.c を作成 # 3. make lib を実行 # # これで、LD_LIBRARY_PATH および CLASSPATH を . に設定して #、java MPtest を実行できるようになる JAVA_HOME=/usr/java JH_INC1=${JAVA_HOME}/include JH_INC2=${JAVA_HOME}/include/solaris MPtest: ${JAVA_HOME}/bin/javac MPtest.java (CLASSPATH=. ; export CLASSPATH ; ${JAVA_HOME}/bin/javah -stubs MPtest) (CLASSPATH=. ; export CLASSPATH ; ${JAVA_HOME}/bin/javah -jni MPtest) lib: cc -G -I${JH_INC1} -I${JH_INC2} NativeThreads.c -lthread ¥ -o libNativeThreads.so clean: rm -rf *.class libNativeThreads.so *.h MPtest.c |
表 3-3 は、Java スレッド API と Solaris および POSIX スレッド API をできるかぎり対応づけたものです。Java スレッド API と Solaris スレッド API および POSIX スレッド API との完全な対応を示すものではありません。また、この表を参考にすれば、Solaris または POSIX スレッドプログラムを簡単に Java スレッドプログラムに、あるいは Java スレッドプログラムを Solaris スレッドプログラムまたは POSIX スレッドプログラムに簡単に変換できるというわけでもありません。表 3-3 は、単に API 間のおおよその対応を示すだけです。3 つの API のうちのいずれかに関する知識があり、各 API 間の対応を確認するときのガイドラインとして利用してください。C の手続き型および階層プログラミングで Solaris API を使用することと、Java のオブジェクト指向のプログラミング手法で Solaris API を使用することの間には、概念上の違いがあります。
次に、Java スレッド API と Solaris スレッド API とは完全には対応していない理由を示す例を紹介します。
表 3-3 では、Java スレッドの破壊スレッド (destroy()) は、POSIX の pthread_cancel() に対応しています。ただし、POSIX の pthread_cancel() は、取り消し点の概念が存在し、かつ pthread_cleanup_push() と pthread_cleaup_pop() を使用して、取り消し点のあたりでクリーンアップハンドラを作成することによって初めて完全になります。Java スレッド API には、スレッドの破壊について同様の概念はありません。この意味で、これら 2 つの破壊手法はまったく異なります。
JDK 1.1 では、destroy() メソッドは推奨されていません。
表 3-3 では、Java スレッドの interrupt() メソッドは POSIX の phthread_kill() に対応していますが、両者はまったく異なります。Java には安全な割り込み点の概念がありますが (たとえば wait())、POSIX にはそのような概念はありません。
Java には、Solaris の読み取り/書き込みロックインタフェースや POSIX の属性に相当するインタフェースはありません。
Java スレッド API |
Solaris スレッド API |
POSIX スレッド API |
thr_create() | pthread_create() | |
activeCount() | ||
checkAccess() | ||
countStackFrames() | ||
currentThread() | thr_self() | pthread_self() |
destroy() | pthread_cancel() | |
dumpStack() | ||
enumerate() | ||
getName() | ||
getPriority() | thr_getprio() | pthread_ getschedparam() |
getThreadGroup() | ||
interrupt() | thr_kill() | pthread_kill() |
interrupted() | ||
isAlive() | ||
isDaemon() | ||
isInterrupted() | ||
join() | thr_join() | pthread_join() |
resume() | thr_continue() | |
run() | ||
setDaemon() | THR_DAEMON フラグ | |
setName() | ||
setPriority() | thr_setprio() | pthread_ setschedparam() |
sleep() | sleep() | sleep() |
start() | ||
stop() | ||
suspend() | ||
同期メソッド | ||
wait() | cond_wait() | pthread_cond_wait() |
notify() | cond_signal() | pthread_cond_signal() |
synchronized メソッド synchronized 文 |
mutex_name() |
pthread_mutexe_name() |
次に示すメソッドは、スレッドグループに作用します。Java には、スレッドグループ機能がありますが、Solaris や POSIX にはありません。
Java WorkShop (JWS) は、Java 開発者向けの強力なビジュアル開発ツールです。Java アプレットやアプリケーションを短時間に簡単に作成するための使いやすいツール群で構成されています。
JWS では専用の Java インタプリタが使用されます。JWS は、表 3-4 に示すアプリケーションで構成されています。
表 3-4 Java WorkShop アプリケーション
ポートフォリオマネージャ |
Java プロジェクトのポートフォリオを作成したりカスタマイズしたりします。新しいアプレットやアプリケーションを作成する元となるオブジェクトやアプレットを管理します。 |
プロジェクトマネージャ |
プロジェクトのディレクトリなどの各種設定を行ったり、保存したりします。これによって、開発者はプロジェクの各構成要素へのパスを覚えておかなくても済みます。 |
ソースエディタ |
ソースコードを作成し編集するためのポイント&クリック方式のツールです。作成、コンパイル、デバッグ中に、Java WorkShop の他のアプリケーションと連動して呼び出されることもあります。 |
構築ツール |
Java ソースコードを Java バイトコードにコンパイルし、ソースコード内のエラーを検出します。構築ツールが検出したエラーとリンクさせてソースエディタを起動するので、開発ysaはすばやくエラーを修正して再コンパイルすることができます。 |
ソースブラウザ |
プロジェクト中のすべてのオブジェクトのクラス継承情報を示すツリー図を表示します。また、プロジェクト中のすべてのコンストラクタと一般的なメソッドを一覧表示し、文字列やシンボルを検索することができます。ソースエディタにリンクしてソースコードを表示することができます。 |
デバッガ |
デバッグプロセスを制御し管理します。作成したアプリケーションまたはアプレットを今トールパネルを使用して実行することによって、スレッドの一時停止や実行再開、ブレークポイントの設定、例外のトラップ、スレッドのアルファベット順表示、メッセージの確認、などを行うことができます。 |
プロジェクトテスタ |
アプレットビューアと同様に、アプレットを実行して動作をテストすることができます。構築ツールを使用してアプレットをコンパイルしてから、プロジェクトテスタでアプレットを実行してください。 |
GUI ビルダー |
ポイント&クリック式のユーザーインタフェースを使用して Java の GUI アプリケーションを作成することができる、統合 GUI ビルダーです。カスタマイズ可能な GUI ウィジェットのパレットが提供されています。 |
オンラインヘルプ |
プロジェクトおよびポートフォリオ、ソースコードの編集、プロジェクトの構築やデバッグ、ブレークポイント、GUI ビルダー、などについての説明のほかに、Java WorkShop の基本的な使い方をひととおり学習できるチュートリアルが含まれています。目次や索引もあります。 |
JWS についての詳細は、http://www.sun.com/workshop/java/ または http://www.sun.co.jp/workshop/jws/ を参照してください。