Javaスレッドの紹介

この記事は、JavaWorldによって最初に公開されたものの1つであり、スレッドの一般的な概要から始めて、Javaプログラミング言語でスレッドを実装する方法について説明します。

簡単に言えば、スレッドはプログラムの実行パスです。今日作成されたほとんどのプログラムは単一のスレッドとして実行されるため、複数のイベントまたはアクションを同時に発生させる必要がある場合に問題が発生します。たとえば、プログラムがキーストロークの読み取り中に絵を描くことができないとしましょう。プログラムは、一度に複数のイベントを処理する機能がないキーボード入力に完全に注意を払う必要があります。この問題の理想的な解決策は、プログラムの2つ以上のセクションを同時にシームレスに実行することです。スレッドを使用すると、これを行うことができます。

Javaスレッドについて学ぶ

この記事は、JavaWorldテクニカルコンテンツアーカイブの一部です。Javaスレッドと同時実行性の詳細については、以下を参照してください。

Javaスレッドを理解するJava 101シリーズ、2002年):

  • パート1:スレッドとランナブルの紹介
  • パート2:スレッドの同期
  • パート3:スレッドのスケジューリングと待機/通知
  • パート4:スレッドグループとボラティリティ

関連記事

  • ハイパースレッディングJava:Java同時実行APIの使用(2006)
  • マルチスレッドプログラムの監視の改善(2007)
  • アクターの並行性を理解する、パート1(2009)
  • 吊り糸の検出と取り扱い(2011)

JavaWorldサイトマップ検索エンジンも確認してください。

マルチスレッドアプリケーションは、単一のプログラム内で多数のスレッドを同時に実行することにより、強力な能力を発揮します。論理的な観点から、マルチスレッドとは、単一のプログラムの複数の行を同時に実行できることを意味しますが、プログラムを2回起動して、同時に実行されているプログラムの複数の行があると言うことと同じではありません。時間。この場合、オペレーティングシステムは、プログラムを2つの別個の別個のプロセスとして扱います。 Unixでは、プロセスをフォークすると、コードとデータの両方に異なるアドレス空間を持つ子プロセスが作成されます。しかしながら、fork()オペレーティングシステムに多くのオーバーヘッドが発生し、CPUを非常に集中的に使用する操作になります。代わりにスレッドを開始することにより、親からの元のデータ領域を共有しながら、効率的な実行パスが作成されます。データ領域を共有するというアイデアは非常に有益ですが、後で説明するいくつかの懸念事項があります。

スレッドの作成

Javaの作成者は、スレッドを作成する2つの方法を丁寧に設計しました。インターフェースの実装とクラスの拡張です。クラスの拡張は、Javaが親クラスからメソッドと変数を継承する方法です。この場合、単一の親クラスからのみ拡張または継承できます。 Java内のこの制限は、スレッドを作成する最も一般的な方法であるインターフェースを実装することで克服できます。 (継承するという行為は、単にクラスをスレッドとして実行することを許可するだけであることに注意してください。start()実行するのはクラス次第などです。)

インターフェイスは、プログラマーがクラスの基礎を築く方法を提供します。これらは、実装する一連のクラスの要件を設計するために使用されます。インターフェイスがすべてを設定し、インターフェイスを実装する1つまたは複数のクラスがすべての作業を行います。インターフェイスを実装するクラスの異なるセットは、同じルールに従う必要があります。

クラスとインターフェースの間にはいくつかの違いがあります。まず、インターフェイスには、抽象メソッドや静的最終変数(定数)のみを含めることができます。一方、クラスはメソッドを実装し、定数ではない変数を含めることができます。第二に、インターフェースはメソッドを実装できません。インターフェイスを実装するクラスは、そのインターフェイスで定義されているすべてのメソッドを実装する必要があります。インターフェイスには他のインターフェイスから拡張する機能があり、(クラスとは異なり)複数のインターフェイスから拡張できます。さらに、インターフェイスはnew演算子でインスタンス化できません。たとえば、Runnable a=new Runnable();許可されていません。

スレッドを作成する最初の方法は、単にThreadクラスから拡張することです。これは、スレッドとして実行する必要のあるクラスを別のクラスから拡張する必要がない場合にのみ実行してください。Threadクラスは、私たちのクラスはその定義を認識しているようにインポートする必要がパッケージjava.langで定義されています。

import java.lang.*; public class Counter extends Thread { public void run() { .... } }

上記の例では、クラスCounterを拡張し、独自の実装のためThreadThread.run()メソッドをオーバーライドする新しいクラスを作成します。このrun()メソッドは、Counterクラススレッドのすべての作業が行われる場所です。Runnableを実装することで、同じクラスを作成できます。

import java.lang.*; public class Counter implements Runnable { Thread T; public void run() { .... } }

ここでは、抽象run()メソッドがRunnableインターフェースで定義され、実装されています。Threadクラスの変数としてクラスのインスタンスがあることに注意してくださいCounter。 2つのメソッドの唯一の違いは、Runnableを実装することにより、クラスの作成の柔軟性が向上することですCounter。上記の例ではCounter、必要に応じてクラスを拡張する機会がまだ存在します。スレッドとして実行する必要がある作成されたクラスの大部分は、おそらく別のクラスから他の機能を拡張しているため、Runnableを実装します。

Do not think that the Runnable interface is doing any real work when the thread is being executed. It is merely a class created to give an idea on the design of the Thread class. In fact, it is very small containing only one abstract method. Here is the definition of the Runnable interface directly from the Java source:

package java.lang; public interface Runnable { public abstract void run(); }

That is all there is to the Runnable interface. An interface only provides a design upon which classes should be implemented. In the case of the Runnable interface, it forces the definition of only the run() method. Therefore, most of the work is done in the Thread class. A closer look at a section in the definition of the Thread class will give an idea of what is really going on:

public class Thread implements Runnable { ... public void run() { if (target != null) { target.run(); } } ... }

From the above code snippet it is evident that the Thread class also implements the Runnable interface. Thread.run() checks to make sure that the target class (the class that is going to be run as a thread) is not equal to null, and then executes the run() method of the target. When this happens, the run() method of the target will be running as its own thread.

Starting and stopping

Since the different ways to create an instance of a thread are now apparent, we will discuss the implementation of threads beginning with the ways available to start and stop them using a small applet containing a thread to illustrate the mechanics:

CounterThread Example and Source code

The above applet will start counting from 0 displaying its output to both the screen and the console. A quick glance might give the impression that the program will start counting and display every number, but this is not the case. A closer examination of the execution of this applet will reveal its true identity.

In this case, the CounterThread class was forced to implement Runnable since it extended the class Applet. As in all applets, the init() method gets executed first. In init(), the variable Count is initialized to zero and a new instance of the Thread class is created. By passing this to the Thread constructor, the new thread will know which object to run. In this case this is a reference to CounterThread. After the thread is created it needs to be started. The call to start() will call the target's run() method, which is CounterThread.run(). The call to start() will return right away and the thread will start executing at the same time. Note that the run() method is an infinite loop. It is infinite because once the run() method exits, the thread stops executing. The run() method will increment the variable Count, sleep for 10 milliseconds and send a request to refresh the applet's display.

Note that it is important to sleep somewhere in a thread. If not, the thread will consume all CPU time for the process and will not allow any other methods such as threads to be executed. Another way to cease the execution of a thread is to call the stop() method. In this example, the thread stops when the mouse is pressed while the cursor is in the applet. Depending on the speed of the computer the applet runs on, not every number will be displayed, because the incrementing is done independent of the painting of the applet. The applet can not be refreshed at every request, so the OS will queue the requests and successive refresh requests will be satisfied with one refresh. While the refreshes are queuing up, the Count is still being incremented but not displayed.

Suspending and resuming

Once a thread is stopped, it cannot be restarted with the start() command, since stop() will terminate the execution of a thread. Instead you can pause the execution of a thread with the sleep() method. The thread will sleep for a certain period of time and then begin executing when the time limit is reached. But, this is not ideal if the thread needs to be started when a certain event occurs. In this case, the suspend() method allows a thread to temporarily cease executing and the resume() method allows the suspended thread to start again. The following applet shows the above example modified to suspend and resume the applet.

public class CounterThread2 extends Applet implements Runnable { Thread t; int Count; boolean suspended; public boolean mouseDown(Event e,int x, int y) { if(suspended) t.resume(); else t.suspend(); suspended = !suspended; return true; } ... }

CounterThread2 Example and Source code

アプレットの現在の状態を追跡するために、ブール変数suspendedが使用されます。一部のメソッドは、間違った状態で呼び出された場合に例外をスローするため、アプレットのさまざまな状態を区別することが重要です。たとえば、アプレットが開始および停止されている場合、start()メソッドを実行するとIllegalThreadStateException例外がスローされます。