Javaスレッドの深い理解
私たちは、一つのスレッドがタスクを実行するために使用することができることを知っており、タスクの実行は非同期であり、背後にあるコードをブロックしません。Javaプロセスでは、クラスのメインメソッドがスレッドで実行されている含まれています。実際には、場合、プロセスは、全体的な対応手順に影響を与えないために、時間のかかる操作を必要とし、これは通常、スレッドで時間のかかる操作は、非同期で実行されます。しかし、スレッドは、タスクの非同期実行にそれを達成する方法ですか?この記事では、実行の必要な秘密のスレッドを得るためには、Threadクラスを理解するだろう。
スレッドの「Java仮想マシンの深い理解」によると、私たちはJavaで対応するスレッドのスレッドオペレーティングシステムということを学びました。オペレーティングシステムスレッドは、無制限の理由の一つであるスレッドを作成することはできません、希少資源である理由をスレッドプールの使用。
また、Javaでスレッドを達成するために、二つの方法があることを知っています:
- Threadクラスの継承
- Runnableを実装します
いずれにせよ、しかし、まだスレッドのstart()メソッドを呼び出すことにより、最後のスレッドを実行する必要があります
Threadクラスの重要なプロパティとメソッドで見てみましょう:
// target就是一个传递给Thread等待Thread执行的Runnable对象
/* What will be run. */
private Runnable target;
/* The group of this thread */
private ThreadGroup group;
// 类方法,该方法会在Thread类初始化时,在类的构造器<clinit>中被调用,且只会调用一次,该方法主要的作用是注册一些本地的方法,以便后期可以使用
/* Make sure registerNatives is the first thing <clinit> does. */
private static native void registerNatives();
static {
registerNatives();
}
// 注册了以下本地方法:
public static native Thread currentThread();
public static nativevoid yield();
public static native void sleep(long millis) throws InterruptedException;
private native void start0();
private native boolean isInterrupted(boolean ClearInterrupted);
public final native boolean isAlive();
public static native boolean holdsLock(Object obj);
private native static StackTraceElement[][] dumpThreads(Thread[] threads);
private native static Thread[] getThreads();
private native void setPriority0(int newPriority);
private native void stop0(Object o);
private native void suspend0();
private native void resume0();
private native void interrupt0();
private native void setNativeName(String name);
复制代码
何このコードの意志の出力で見てみましょう:
public static class MyThread extends Thread{
@Override
public void run(){
System.out.println("MyThread---1");
}
}
public static class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("MyRunnable---1");
}
}
public static void main(String[] args) {
Thread t1 = new MyThread();
Thread t2 = new Thread(new MyRunnable());
t1.start();
t2.start();
System.out.println("MyThread---2");
System.out.println("MyRunnable---2");
}
复制代码
出力コードの内容が不確実で出力してもよいです。
MyThread---2
MyRunnable---2
MyRunnable---1
MyThread---1
复制代码
出力も可能です。
MyThread---1
MyRunnable---1
MyThread---2
MyRunnable---2
复制代码
しかしながら、上記のコードt1.start()、t2.start()の場合:
t1.run();
t2.run();
复制代码
その後、出力が決定した次のようになります。
MyThread---1
MyRunnable---1
MyThread---2
MyRunnable---2
复制代码
なぜ)(スタート、出力の内容が不確実である、と実行の使用()の出力は、それを決定するのですか?このプロセスは、スレッドを理解するために最初から開始する必要があります。次のようにスレッドクラスのソースコードのstart()メソッドは次のとおりです。
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
private native void start0();
复制代码
()メソッドは、実際に)メソッドは、(のネイティブをSTART0呼び出す開始の内側に見ることができます。Threadクラスの初期化は、メソッドRegisterNativesをを実行する方法START0実用的な関連性がJVM_StartThread方法である()、ローカルメソッドを登録します。
{"start0", "()V",(void *)&JVM_StartThread}
复制代码
jvm.cppで、次のコードセグメント:
JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread)){
...
native_thread = new JavaThread(&thread_entry, sz);
...
}
复制代码
ここJVMENTRYは、以下のように、そのスレッド機能がthread_entryある内JVMStartThread機能を定義するマクロは、あなたが本当にスレッドローカルプラットフォーム固有の関数を作成するために見ることができます:
static void thread_entry(JavaThread* thread, TRAPS) {
HandleMark hm(THREAD);
Handle obj(THREAD, thread->threadObj());
JavaValue result(T_VOID);
JavaCalls::call_virtual(&result,obj,KlassHandle(THREAD,SystemDictionary::Thread_klass()),
vmSymbolHandles::run_method_name(), //调用了run_method_name
vmSymbolHandles::void_method_signature(),THREAD);
}
复制代码
あなたは、コールvmSymbolHandles :: runmethodname方法、およびマクロ定義とvmSymbols.hppでrunmethodnameを見ることができます:
class vmSymbolHandles: AllStatic {
...
// 这里决定了调用的方法名称是 “run”
template(run_method_name,"run")
...
}
复制代码
上記のコードから、スレッドの実行開始()メソッドは、最初のスレッドが次に得られ、新しいオペレーティングシステムのスレッドを作成するときに、オペレーティング・CPUタイムスライス、実行コールバックメソッドの実行()、そのあなただけの唯一の通常のスレッドオブジェクトを実行するためにrun()メソッドを通じて開始()新しいスレッドと実行本体の非同期スレッドによって証明されるように作成することができ、かつ並列に実行されませんが、シリアル実行。
以下は、あなたのスレッドに関連付けられている一般的な問題のいくつかを送っています。
的睡眠を通し、参加し、歩留まり
- 1.sleep
- 睡眠は()ので、CPU使用率が他のスレッドのためにいくつかの時間を残していることを、現在の停滞状態にスレッド(現在のスレッドをブロック)を行います
- 睡眠は、オブジェクトロックのスリープを解除しないとき
- 2.joinに1つのスレッドで実行されるが、方法AスレッドBを結合し、Aは、Bがフォローアップ作業を実施した後に終了するのを待っている、中断され
public static void main(String[] args){
Thread t1 = new Thread();
t1.start();
t1.join();
// 以下代码会在t1执行完毕后打印
System.out.println("t1 finished");
}
复制代码
- 3.yield
- 収量は平均終了し、一時停止しませんが、誰かがスレッドのスケジューリングを伝えるために必要がある場合、あなたはそれを取ることができ、私はあまりにもなし、再び1つのニーズを実行します、私は続けます
- ロックが解除されていないコール利回り
対象的待ち、通知、のnotifyAll
- 1.wait
- 待機()メソッドはクラスオブジェクトのメソッドであり、スレッドが待機する場合に実行()メソッド
- ロックオブジェクトを失うながらスレッドが再び覚醒するロックを獲得するために、プールに関連した待ち時間とオブジェクトに入ります
- 待機()通知()または使用のnotifyAll()、または現在のプールで待機しているスレッドを覚ますためにスリープ時間を指定します
- 待ち時間は()、それ以外の場合は「java.lang.IllegalMonitorStateExceptionを」エラーになり、同期ブロックに配置する必要があります
- 2.notify
「対象のモニター」で動作しなければならない)((待つ)と通知
Runnable1 implements Runnable{
public void run(){
synchronized(lock){
// 等待其他线程来唤醒
lock.wait();
System.out.println("Runnable1 has been notified by other thread");
}
}
}
Runnable2 implements Runnable{
public void run(){
synchronized(lock){
System.out.println("Runnable2 will notify other thread who wait for lock");
// 唤醒其他线程 lock.notify();
}
}
}
public static void main(String[] args){
Object lock = new Object();
Thread t1 = new Thread(new Runnable1(lock));
Thread t2 = new Thread(new Runnable2(lock));
t1.start();
t2.start();
}
复制代码
違いのスレッドとRunnableを
- スレッドの開始により、Runnableを(実際にターゲットのrunメソッドと呼ばれる)を開始し、スレッドターゲット属性が実行可能である一方、
- Runnableをプロパティは、リソースの共有を実現することができ、スレッドがリソースを共有することはできません
MyRunnable r = new MyRunnable();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
// t1/t2线程操作的都是同一个实例r,所以r中的数据可以实现多线程共享
t1.start();
t2.start();
复制代码
スレッド間で通信する方法
- 参加:別のスレッドのスレッドの待機を実行する前に終了します
- 待機/通知:別のスレッドのスレッドの待機を使用すると、実行後のオブジェクトのモニターを所有してウェイク
- たCountDownLatch:待機するスレッド(countDownLatch.await())他のスレッドの実行の任意の数の(countDownLatch.countDown())を実行した後に完了されます
- CyclicBarrierを:それらの調製の前にすべてのスレッドは、Unityがすべてのスレッドが準備ができている時にフォローアップし始めた(すべては)(cyclicBarrier.awaitを呼び出します)
- セマフォ:あなたが取得してライセンスを取得し、同時にアクセスするスレッドの数を制御することができます()、待機していない場合、および(リリース)は、許可証をリリース
- Aコーラブル:サブスレッドの実行結果は、親スレッドへのバックアップ
FutureTask<Integer> futureTask = new FutureTask<>(callable);
new Thread(futureTask).start();
Object result = futureTask.get();
复制代码
- たCountDownLatchとスレッド間CyclicBarrierを待ちを達成することができますが、彼らは別の強調を持っています:
- たCountDownLatch Aは、一般タスクを実行するいくつかの他のスレッドを待機するスレッドのために使用した後に、それが実行されました。
- CyclicBarrierをは、一般的に特定の状態に互いに待つ、次いで同時にスレッドのグループを実行するスレッドのグループのために使用されます。
- また、たCountDownLatchを再利用することができず、CyclicBarrierを再利用することができます。
- 実際には、セマフォとやや似たロックが、一般的に、リソースの特定のセットへのアクセスを制御するために使用されます。
原理スレッドプール
スレッドプールは、2つのパラメータがあります:カーネルスレッドの数とスレッドの最大数coreNum MAXNUM
初期スレッドプールを想定スレッドのコアの数が5で、スレッドの最大数は10であり、どのスレッドがない場合、スレッドプールを初期化します
タスクの後に来たとき、あなたは、ジョブが再び、その後、スレッドを初期化する場合は、最初の6つのタスクが来た場合、連続スレッドは、5を初期化し、スレッドを初期化します
その後、ブロッキングキューに第六ミッションは意志
1つのスレッドがアイドル状態の場合さて、スレッドプール内の5つのスレッドを持っている、それが実行するためにキューをブロックから最初の6つのタスクを取得します
5つのスレッドプールのスレッドが状態を実行している場合は、最初のタスクは、ブロッキングキューに格納されています
キューがいっぱいになると、私たちは、スレッドの最大数は10ですが、プールで唯一の5つのスレッドは、6つのスレッドプールのスレッドで、ブロッキングを実行するための新しいスレッドをタスクキューに保存することはできません。この時間を作成し、設定します
プール内のスレッドの数が10に達し、かつ、キューの遮断がいっぱいになった場合は、カスタムすることにより、これらの作業に行くことができる機能を拒否
一定の期間のために最後に実行した後、タスク実行のキューを遮断することが終わって、スレッドプール内のスレッドの数は、コアスレッドを超えて自動的にアイドル時間の期間内に回収されます