Javaのスレッドのコンストラクタをマルチスレッド(ソースコード分析)

スレッドのライフサイクルの状態と、共通のスレッドAPIの以前の記事では、語り手がありました。この記事では、その施工方法の説明に集中するようになった私たちは、startメソッドは、スレッドを開始することができるようになります呼ぶ理由、それはまた発表されます。

まず、デーモンスレッドと非デーモンスレッド

私たちは、スレッドを実行しているJava仮想マシンがされるときにデフォルトが私たちのサービスのためのスレッドにいくつかの他のスレッドを開始するので、これは、スレッドIDは、すべての時間が0でない時があります取得します。そして、デフォルトで作成されたスレッドは区別がある私たち自身を作成し​​ます。非デーモンデーモンスレッドとスレッド間を区別する必要があります。

1.デーモンスレッドと非デーモンスレッドとは何ですか?

これらのデフォルトのスタートアップのスレッドがデーモンスレッドで、彼は、特にいくつかの背景の仕事を扱います。たとえば、ガベージコレクションのように。これらの非デーモンスレッドは、私たち自身の創造のスレッドです。公式ドキュメントは、Java仮想マシンなし非デーモンスレッド、およびスレッドは、デフォルトを終了するとき、と述べています。たとえば、あなたは理解することができます:

顧客のようなホテルのスタッフ、非デーモンスレッド内のデーモンスレッドと同様に、顧客がない、そしてもう必要なしアテンダントはありません。

2、コードが示します

私たちは、自分の役割を発揮するために、コードに目を通します

public class Test {
	public static void main(String[] args) {
		Thread thread = new Thread(()->{
			while (true) {
				System.out.println("无限循环");
			}
		}) ;
		thread.start();
	}
}
复制代码

ここでは、2つのメインスレッドがメインスレッドがされている、2番目のスレッドが自分で作成しています。実行した後は、スレッドは、非デーモンスレッドであるため、プログラムの実装は、ラジオで行くことは明らかです。実行のさえ、メインスレッドは、スレッドの実行が終了となります。今、私たちは、デーモンスレッドが同じではないので、スレッドを設定します。

public class Test {
	public static void main(String[] args) {
		Thread thread = new Thread(()->{
			while (true) {
				System.out.println("无限循环");
			}
		}) ;
		//设置为守护线程
		thread.setDaemon(true);
		thread.start();
	}
}
复制代码

再び走行では、我々は、スレッドがデーモンスレッドになりました設定したため、それがあり、通常の手順のうち、それを見つけるでしょう、あなたは今、ウェイター、ノー顧客となっているメインスレッドとスレッドを考えるので、これらのデーモンスレッドが買い物をします私の周り徘徊して放置しました。

3、デーモンスレッドのシナリオ?

あなたがデーモンスレッドの特性を理解した後、あなたは、このようなJVMを終了することも従うことを、いくつかのスレッドを撤回したい場合など、予期しない何かを、行うには、この原理を使用することができ、あなたは彼がデーモンスレッドとして設定置くことができます。

コンセプトのこの基本的な理解の後、我々はコンストラクタのスレッドを見てください。

第二に、スレッドのコンストラクタ

1、コンストラクタ

スレッドスレッドのコンストラクタは、8の合計を持っています

ここでは、新しいクラスのThreadGroupに接触します。それが表す意味することは、スレッドがスレッドグループに属しています。スレッドグループに属するスレッドを指定することができるいずれかのときに我々は、スレッドの例で見ることができる上に、またその実行可能なインターフェースを宣言することができます。のは、このスレッドグループのThreadGroupを分析してみましょう。

彼ら二人の関係はこのように表現することができます。

上記では、我々はここで我々はあなたのコードを示し、スレッドグループのスレッドを指定することができました。

public class Test {
	public static void main(String[] args) {
		//为当前线程设置线程组
		ThreadGroup group= new ThreadGroup("线程组");
		Thread thread = new Thread(group,"当前线程");
		thread.start();
		System.out.print(thread.getThreadGroup().getName());
	}
}
//输出:线程组
复制代码

これは、その基本的な使い方ですが、私たちは、指定されたスレッドグループの出力スレッドに属していない場合はどのような結果でしょうか?テスト:

public class Test {
	public static void main(String[] args) {
		ThreadGroup group= new ThreadGroup("线程组");
		ThreadGroup maingroup = Thread.currentThread().getThreadGroup();
		
		Thread thread1 = new Thread(group,"线程A");
		Thread thread2 = new Thread("线程B");
		System.out.println(thread1.getThreadGroup().getName());
		System.out.println(thread2.getThreadGroup().getName());
		System.out.println(maingroup.getName());
	}
}
//输出:线程组 main main
复制代码

上記のコードはこれである意味、スレッドを指定して、我々が作成したスレッドグループ、デフォルトのスレッドBのスレッドグループ、maingroupは、メインスレッドグループです。出力は、あなたが彼と彼の父のスレッドグループが同じで、スレッドのスレッドグループを指定しない場合、我々は、見つけることを示しています。

2、インスタンス化スレッド

8つのコンストラクタスレッド上で考えると、我々はスレッドをインスタンス化するためにこれらの8つのコンストラクタを使用することができますが、一番下には、それを行う方法ですか?それはそれはそれの一例として、一般的なカテゴリとしてだろうか?この点で、私たちは、深いソーススレッドを見てする必要があります。

public Thread(ThreadGroup group, Runnable target, String name, long stackSize) {
    init(group, target, name, stackSize);
}
复制代码

私たちは、他の工法がのサブセットであるため、最も複雑な施工方法のいずれかを選んだ、私たちはここで見ることができ、実際、本当に初期化がinitメソッドで行われているinitメソッドを呼び出しています。我々としても表情で行くかもしれません:

private void init(ThreadGroup g, Runnable target, String name,long stackSize) {
    init(g, target, name, stackSize, null, true);
}
复制代码

initメソッドは、一つが、パラメータのうち二つ以上あります。我々が行くと見て話をする必要が知りたいです:

    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
        //第一部分:确保线程名字不为空
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }
        this.name = name;
        //第二部分:指定线程组
        Thread parent = currentThread();
        SecurityManager security = System.getSecurityManager();
        if (g == null) {
            if (security != null) {
                g = security.getThreadGroup();
            }
            if (g == null) {
                g = parent.getThreadGroup();
            }
        }
        g.checkAccess();
        if (security != null) {
            if (isCCLOverridden(getClass())) {
                security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
            }
        }
        g.addUnstarted();
        this.group = g;
        //第三部分:一些其他参数设置
        this.daemon = parent.isDaemon();
        this.priority = parent.getPriority();
        if (security == null || isCCLOverridden(parent.getClass()))
            this.contextClassLoader = parent.getContextClassLoader();
        else
            this.contextClassLoader = parent.contextClassLoader;
        this.inheritedAccessControlContext =
                acc != null ? acc : AccessController.getContext();
        //第四部分:runnable接口配置
        this.target = target;
        setPriority(priority);
        if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        //第五部分:设置栈大小
        this.stackSize = stackSize;

        //第六部分:设置线程ID
        tid = nextThreadID();
    }

复制代码

最後に、我々は、スレッドを初期化する方法を見つけました。私たちは5つの部分に分かれて:

(1)パートI:スレッド名を空にすることはできません、

ここでは1スレッドの名前を言及しなければならない、Javaの公式により、開発者は、スレッドの名前を指定するには、表示されない場合、スレッドはスレッドの名前から成る、デジタル接尾辞に接頭辞「スレッド」になります私たちに尋ねました。しかし、いずれにしても、我々はスレッド名を持っている必要があります。

(2)パートII:現在のスレッドのスレッドグループを指定します。

コードの中にgが空でない場合、我々はそれ以外の場合は、スレッドグループの親クラスを使用しますが、スレッドグループg現在のスレッドとしてこれを使用していることを、非常に明確です。もちろん、途中でなく、その上のアクセス許可の問題をチェックして。

(3)パートIII:その他の設定

ここでの設定は、その上のスレッド、優先順位、クラスローダとのデーモンに設定されています。

(4)パートIV:実行可能なインターフェース構成

指定は、Runnableインタフェースクラスを実装します。

(5)パートV:スタックサイズを設定します。

スレッド・スタック・サイズは、スレッドスタックの、すなわちデフォルトのサイズ、引数ゼロ転写プロセスのデフォルトのサイズに応じていることがわかります

(6)パートVI:スレッドIDを設定します

スレッドIDはnextThreadIDメソッドで指定されています。私たちは、スレッドIDを指定する方法を見てすることができます。

private static synchronized long nextThreadID() {
    return ++threadSeqNumber;
}
复制代码

私たちは、実際には、threadSeqNumberを見ることができます。

OK、上記のスレッドを初期化する方法で、私たちは非常に明確であると信じて、このコンストラクタのinitメソッドのその他のパラメータはそれにいくつかの変更を行いました。しかし、原理は同じです。前回の記事で言及した一つの問題は、まだ読み、解決されていません。

呼ばれる第三に、なぜ起動方法は、スレッドを開始することができるようになります

この問題を解決するために、我々はまた、深さ(jdk1.8)にソースコードを見ています:

   public synchronized void start() {
        if (threadStatus != 0)
            throw new IllegalThreadStateException();
        group.add(this);
        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
            }
        }
    }
复制代码

これらのコードは何を意味するのですか?最初のスレッドの状態が異常であるか否かを決定する、現在のスレッドは、スレッドの最後の呼び出しを開始する開始、START0フォーマルメソッドの前にスレッドグループに追加されます。今ここで重要なのは、我々は同様の外観を引くに行くかもしれないSTART0方法これは本当に始めたスレッドです。

それが起動するネイティブメソッドの本当のスタートです、runメソッドは、それのコンテンツ内で実行される理由は何のメソッド呼び出しは、実行しないように思われます。公式文書が説明されたので:JNIメソッドは内部で実行START0メソッドを呼び出します。このような文は、上記の理由を説明することです。

すでに二つの問題を解決したように、第一は、コンストラクタであり、我々は、startメソッドは、スレッドを開始するのではなく実行することができます呼ん秒も理解しています。我々は、APIメソッドは、実質的にすべてのネイティブのスレッドを提供し、ソースコードを知ることができる分析します。

おすすめ

転載: juejin.im/post/5d5bb572f265da03bd051eb9