1. スレッド
スレッドに関しては、誰もがよく知っているはずです。スレッドはプロセス内のエンティティです。スレッド自体は独立して存在しません。プロセスは、データ セット上でコードを実行するアクティビティです。システムの基本単位です。リソースの割り当てとスケジューリング: スレッドは実行パスであり、プロセスには少なくとも 1 つのスレッドがあり、プロセス内の複数のスレッドはプロセスのリソースを共有します。
オペレーティングシステムがリソースを割り当てるとき、プロセスにリソースを割り当てますが、CPU リソースは特殊で、スレッドに割り当てられるため、実際に CPU リソースを占有して実行されるのはスレッドであるため、スレッドは CPU 割り当ての基本単位でもあります。
Java では、main メソッドを開始すると、実際には JVM プロセスが開始され、main 関数が配置されているスレッドがこのプロセスのメイン スレッドになります。
こんなにくだらない話をしても、まだ絵として理解するのは簡単ではありません。
絵はあまり良くありません
この図からわかるように、プロセスには複数のスレッドがあり、複数のスレッドはプロセスとスレッドのメソッド領域とヒープ リソースを共有しますが、各スレッドには独自の独立したプログラム カウンタとスタックがあることがわかります。もう一度質問しますが、プログラム カウンタとスタック、およびヒープとメソッド領域とは何ですか?
1.1 プログラムカウンター(メモ帳)
プログラム カウンタは実際には、現在のスレッドによって記録されたスレッド実行命令のアドレスを記録するために使用されるメモリ領域です。では、なぜプログラムカウンターは非公開なのでしょうか? 前述したように、スレッドは特別であり、CPU 実行の最も基本的な単位であり、CPU は通常、タイム スライス ローテーション方式を使用してスレッドのポーリングを可能にします。そのため、現在のスレッドの CPU タイム スライスが使い果たされた後、CPU は諦めて次のターンを待ちます。自分が実行する場合は実行してください。では、このスレッドがどこで実行されているかをどのようにして知ることができるのでしょうか? 実際、プログラム カウンタは、スレッドが CPU を解放したときに実行するアドレスを記録し、タイム スライスに再割り当てされると、スレッドは独自のプライベート カウンタで指定されたアドレスから実行を継続できます。
1.2スタック(ディープシークレット)
各スレッドには独自のスタック リソースがあり、ローカル変数とカスタム オブジェクトの参照アドレスを保存するために使用されます。これらのローカル変数はスレッドに対してプライベートであり、他のスレッドからアクセスすることはできません。Threadlocal は後で紹介します。素晴らしいです。さらに、スタックは、スレッド呼び出しのスタック フレーム (スレッド呼び出し記録) を格納するためにも使用されます。
1.3 ヒープ (実際のオブジェクトのホーム、このオブジェクトはそのオブジェクトではありません)
ヒープはプロセス内の最大のメモリ部分です。ヒープはプロセス内のすべてのスレッドによって共有され、プロセスの作成時に割り当てられます。ヒープ オブジェクトには主に新しいカスタム オブジェクトの実際のインスタンスが格納されます。
1.4 メソッド領域 (コンパイルされたクラスのホーム)
メソッド領域は、JVM によってロードされたクラス、定数プール、静的変数などの情報を格納するために使用され、スレッドによっても共有されます。ここで、JAVA jdk はバージョンごとにメソッド領域に異なるものを配置することを説明したいと思います。
1.6: ランタイム定数プール、クラスのメタデータ情報、静的変数、コンパイラによってコンパイルされたコードなど。
1.7: 格納場所は基本的に 1.6 と同じですが、定数プールが取り出され、メソッド領域に格納されない点が異なります。
2. JAVAスレッドの作成と操作
Javaにはスレッド作成メソッドが3つあり、Runnableインタフェースを実装するrunメソッド、Threadクラスを統合してrunメソッドを書き換えるメソッド、FutureTaskメソッド(戻り値あり)を使用するメソッドの3つです。
2.1 Runnableインターフェイスの実装
public class ThreadTest {
public static class RunnableTask implements Runnable {
@Override
public void run() {
System.out.println("implements Runnable Thread");
}
}
public static void main(String[] args) {
RunnableTask runnableTask =new RunnableTask();
new Thread(runnableTask).start();
}
}
2.2 Threadクラスの継承
public static class ThreadTask extends Thread{
@Override
public void run() {
System.out.println("extends Thread");
}
}
public static void main(String[] args) {
ThreadTask threadTask =new ThreadTask();
threadTask.start();
}
2.3 Callableインターフェースの実装
//创建一个CallableTask 实现Callable接口中的run方法
public static class CallableTask implements Callable<String>{
@Override
public String call() throws Exception {
return "callable ruturn";
}
}
public static void main(String[] args) {
//FutureTask构造函数传入一个CallableTask实例
FutureTask<String> futureTask=new FutureTask<String>(new CallableTask());
new Thread(futureTask).start();
try {
//等待任务执行完成,获取返回值
String result = futureTask.get();
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
}
上記のコードには一長一短があり、例えば Java では単一継承のみがサポートされており、Runnable を実装して Thread クラスを継承する場合には戻り値がありません。最初の 2 つの実装はタスクの戻り結果を取得できませんが、FutureTask は取得できます。
つづく。。。
参考書籍: JAVA 同時プログラミングの美しさ - Zhai Lianxue Bintian