Javaでスレッドを作成する正しい方法

4636 ワード

Exectorsのスレッドを作成する方法で、作成したスレッドのプールは、ExectorServiceインターフェースを実現しました。常用方法は以下のいくつかあります。
a、newFiexedThreadPool(int Threads):固定数スレッドを作成するスレッド池。
b、newCachedThreadPool():キャッシュ可能なスレッドプールを作成し、executeを呼び出して、以前に作成されたスレッドを再利用します。利用可能なスレッドがない場合は、新しいスレッドを作成し、プールに追加します。終了してキャッシュから60秒以上使用されていないスレッドを削除します。
c、newSingleThreadExector()は、単線化されたExectorを作成する。
d、newScheduledThreadPool(int coree PoolSize)は、タイミングと周期性をサポートするタスクが実行するスレッドプールを作成し、Timerクラスに代わる場合が多い。
見たところ機能はやはり比較的に強大で、また工場のモードを使って、また比較的に強い拡張性があって、重要なのは使うのが更に比較的に便利なので、例えば:
ExectorService exector=Exectors.newFixedThreadPool(nThreads)固定サイズのスレッドを作成できます。
なぜ私はこのクラスを使ってスレッドを作ることを勧めないですか?
Exectorsには何か問題がありますか?アリババJava開発マニュアルでは、Exectorsを使ってスレッドを作成すると、OOM(OutOfMemory、メモリが溢れ出す)の原因になりますが、なぜ説明していませんでしたか?
まず簡単な例を紹介します。Exectorsを使ってOOMを引き起こす状況をシミュレーションしてみます。
public class ExecutorsDemo {
    private static ExecutorService executor = Executors.newFixedThreadPool(15);
    public static void main(String[] args) {
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            executor.execute(new SubThread());
        }
    }
}

class SubThread implements Runnable {
    @Override
    public void run() {
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            //do nothing
        }
    }
}
JVMパラメータを指定します。-Xmx 8 m-Xms 8 m以上のコードを実行すると、OOMをスローします。
Exception in thread「main」java.lang.OutOfMemoryError:GC overhead limit exceded    at java.util.co ncurrent.Linked Blocking Que.offer(Linked Blocking Que.java:416)    at java.util.co ncurrent.ThreadPool Exector.execute(ThreadPool Exector.java:1371)    at comp.hollis.Exectors Demo.main(Exectors Demo.java:16)
以上のコードは、ExectorsDemo.javaの16行目はコードの中のexector.executeです。
Exectorsはなぜ上の例を通して、Exectorsが作成したスレッドプールにOOMのリスクがあるかを知りました。私たちはExectorsのソースコードを深く分析する必要があります。
実は、上記のエラーメッセージの中で、私達はクモの跡を見抜くことができるので、以上のコードの中で実はすでに言って、本当にOOMを招くのは実はLinked BlockingQue.offerの方法です。
Exception in thread「main」java.lang.OutOfMemoryError:GC overhead limit exceded    at java.util.co ncurrent.Linked Blocking Que.offer(Linked Blocking Que.java:416)    at java.util.co ncurrent.ThreadPool Exector.execute(ThreadPool Exector.java:1371)    at comp.hollis.Exectors Demo.main(Exectors Demo.java:16)
コードを見ると、下の階は確かにLinked BlockingQueで実現されています。
public static ExectorService newFixedThreadPool(int nThreads){        return new ThreadPoolExector(nThreads,nThreads,                                      0 L、TimeUnit.MILLISECONS、                                      new Linked Blocking Que();
Javaの渋滞行列について読者が知っていれば、ここを見れば原因がわかるかもしれません。
Javaの中のBlockingQueは主に二つの実現があります。それぞれArayBlockingQueとLinked Blocking Queです。
ArayBlockingQueは、行列で実現される境界閉塞行列であり、容量を設定しなければならない。
Linked BlockingQueはチェーンで実現された有境界ブロック列で、容量はオプションで設定できます。設置しないと無境界のブロック列になります。最大長はInteger.MAX_です。VALE
ここで問題があります。*を設置しないと、境界のない渋滞列になります。最大長はInteger.MAX_です。VALE。***つまり、Linked BlockingQueの容量を設定しないと、デフォルト容量はInteger.MAX_になります。VALE
newFixedThreadPoolでLinked BlockingQueを作成した場合、容量は指定されていません。この時、Linked Blocking Queは境界線のない列であり、境界のない列に対しては、常に列にタスクを追加することができます。この場合、タスクが多すぎてメモリオーバーフローが発生する可能性があります。
上記の問題は主にnewFixedThreadPoolとnewSingleThreadExectorの二つの工場方法に現れています。newCachedThreadPoolとnewScheduledThreadPoolの2つの方法が安全です。この2つの方法で作成された最大スレッド数はInteger.MAX_かもしれません。VALEは、こんなに多くのスレッドを作成すると、必然的にOOMを引き起こす可能性があります。
スレッド池を作成する正確な姿勢は、Exectorsを使用してスレッドを作成することを避け、主にデフォルトの実装を使用することを避けます。作成と同時にBlockQueの容量を指定すればいいです。
private static ExecutorService executor = new ThreadPoolExecutor(10, 10,
        60L, TimeUnit.SECONDS,
        new ArrayBlockingQueue(10));
この場合、提出されたスレッド数が現在の利用可能スレッド数を超えると、java.util.co ncurrent.RejectExecution Exceptionをスローします。これは現在のスレッド池で使用されているキューが境界列であり、列がすでにいっぱいになっているため、新しい要求を処理することができません。しかし、エラーが発生するよりは異常(Exception)のほうがいいです。
自分でThreadPool Exectorを定義する以外に。他に方法があります。この時は最初にソースクラスを思い出すべきです。例えば、apacheやguavaなどです。
著者はGuavaが提供するThreadFactoryBuiderを使ってスレッド池を作成することを推奨します。
public class ExecutorsDemo {

    private static ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
        .setNameFormat("demo-pool-%d").build();

    private static ExecutorService pool = new ThreadPoolExecutor(5, 200,
        0L, TimeUnit.MILLISECONDS,
        new LinkedBlockingQueue(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());

    public static void main(String[] args) {

        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            pool.execute(new SubThread());
        }
    }
}
文章を転載して、予備学を見ます!
原文:https://blog.csdn.net/hollis_chuang/articale/detail/83743723