個人まとめのJAVAスレッド管理

12818 ワード

JAVAのスレッドは主にスレッドプールによって管理されており、その主な意味は優雅なスレッドの閉鎖とスレッドの合理的な運用によって私たちが実行しなければならないいくつかのタスクを処理することである.すなわち,スレッドプールを介して一定数のスレッドを作成し,スレッドプールに渡して管理する.タスクを実行する必要がある場合は、そのスレッドを直接使用します.タスクの実行が完了すると、スレッドは保持され、次のタスクの実行に使用できます.タスクがスレッドより多い場合、タスクはスレッドの空きが出るのを待っている間に、空きスレッドを使用してタスクを実行します.
1.スレッド多重化の例
public class MyThread implements Runnable {
    private String str;
    public MyThread(String s){
        this.str = s;
    }

    @Override
    public void run() {
        System.out.println(str+":"+Thread.currentThread().getName()+" is running!");
    }
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Test {
    public static void main(String[] agrs){
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        executorService.execute(new MyThread("A"));
        executorService.execute(new MyThread("B"));
        executorService.execute(new MyThread("C"));
        executorService.shutdown();
    }
}

実行結果
A:pool-1-thread-1 is running!
B:pool-1-thread-2 is running!
C:pool-1-thread-1 is running!

スレッドプールには2つのスレッドしか割り当てられていません.この2つのスレッドはまずAとBのスレッドを実行し、スレッドAの実行が完了すると、空きスレッドをスレッドCの実行に使用します.
次にExecutorsを見てみましょうNewCachedThreadPool()バッファの例です.バッファは必要に応じて新しいスレッドを作成するスレッドプールですが、以前に構築されたスレッドが使用可能になった場合に再利用されます.作成したスレッドが60秒以上空いている場合は、破棄して削除します.次は直接コードをつけます
public class MyThread implements Runnable {
    private String str;
    public MyThread(String s){
        this.str = s;
    }

    @Override
    public void run() {
            try {
                System.out.println(str + ":" + Thread.currentThread().getName() + " is running!");
                Thread.sleep(500);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
    }
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Test {
    public static void main(String[] agrs){
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(new MyThread("A"));
        executorService.execute(new MyThread("B"));
        executorService.execute(new MyThread("C"));
        try {
            Thread.sleep(1000);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        executorService.execute(new MyThread("D"));
        executorService.execute(new MyThread("E"));
        executorService.shutdown();
    }
}


実行結果
A:pool-1-thread-1 is running!
C:pool-1-thread-3 is running!
B:pool-1-thread-2 is running!
E:pool-1-thread-3 is running!
D:pool-1-thread-2 is running!

Executorsで新CachedThreadPool()はバッファプールを作成し、A,B,Cの3つのスレッドを実行する必要があるため、バッファプールは3つのスレッドを作成し、この3つのスレッドは起動後500ミリ秒で実行され、メインスレッドなどが1秒後になると、バッファプールはこの3つのスレッドを破棄せず、新しいスレッドも作成されません.この3つの空きスレッドを直接DとEの2つのスレッドを実行するために使用します.
2、Callableインタフェース
CallableインタフェースはRunnableインタフェースと似ており、スレッドの実行可能なタスクを定義するために使用されます.CallableはJDK 1.5に導入され、Runnableと比較して3つの利点がある:1、タスクに異常を投げ出すことができる2、タスクを終了することができる3、タスクの戻り値を取得することができるこの時1つの問題が発生する:新旧の2種類のスレッドタスクを定義する方式、どのように前後に互換性があるか?ここでFutureTaskという類が出てきました
ublic class FutureTask implements RunnableFuture {
    //     Callable     
    public FutureTask(Callable callable) { //...}
    //     Runnable     ,result    ,        (futureTask.get()      )。
    public FutureTask(Runnable runnable, V result) {//...}
}

public interface RunnableFuture extends Runnable, Future {
    void run();
}

CallableまたはRunnableをFutureTaskにカプセル化すれば、2つのシーンで使用できます.
次の前のコードの例を示します.
import java.util.concurrent.Callable;

public class MyCallable implements Callable {
    @Override
    public String call() throws Exception{
        int num=0;
        for(int i=0;i<3;i++){
            num++;
            System.out.println("num="+num);
        }
        return String.valueOf(num);
    }
}
import java.util.concurrent.FutureTask;

public class Test {
    public static void main(String[] agrs){
        FutureTask task = new FutureTask(new MyCallable());
        new Thread(task).start();
        System.out.println(getFutureValue(task));
        System.out.println("after task.get()!");
    }

    public static String getFutureValue(FutureTask task){
        String str = "default value";
        try{
            str = task.get();
        }catch (Exception e){
            System.out.println("task is canceled!");
        }
        return str;
    }
}

実行結果
num=1
num=2
num=3
3
after task.get()!

「after task.get()!」この文はgetFutureValue()メソッドの実行後にtask.get()メソッド実行実行.コードを改造する
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        for(int i=0;i<3;i++){
            System.out.println("num="+(i+1));
        }
    }
}
import java.util.concurrent.FutureTask;

public class Test {
    public static void main(String[] agrs){
        String str = "default value";
        FutureTask task = new FutureTask(new MyRunnable(),str);
        new Thread(task).start();
        System.out.println(getFutureValue(task));
        System.out.println("after task.get()!");
    }

    public static String getFutureValue(FutureTask task){
        String str = "default value1";
        try{
            str = task.get();
        }catch (Exception e){
            System.out.println("task is canceled!");
        }
        return str;
    }
}

実行結果
num=1
num=2
num=3
default value1
after task.get()!

Runnableを使用してFutureTaskを構築する場合、futureがわかる.get()はdefaultValueを直接取得します.
3、タスクスレッドの中断
Callableタスクの中断
import java.util.concurrent.Callable;

public class MyCallable implements Callable {
    @Override
    public String call() throws Exception{
        int num=0;
        for(int i=0;i<3;i++){
            num++;
            Thread.sleep(100);
            System.out.println("num="+num);
        }
        return String.valueOf(num);
    }
}

import java.util.concurrent.FutureTask;

public class Test {
    public static void main(String[] agrs){
        FutureTask task = new FutureTask(new MyCallable());
        new Thread(task).start();
        try{
            Thread.sleep(250);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        task.cancel(true);
        System.out.println(getFutureValue(task));
        System.out.println("after task.get()!");
    }

    public static String getFutureValue(FutureTask task){
        String str = "default value";
        try{
            str = task.get();
        }catch (Exception e){
            System.out.println("task is canceled!");
        }
        return str;
    }
}

実行結果
num=1
num=2
task is canceled!
default value
after task.get()!

task.cancel()パラメータがtrueの場合、スレッドはすぐに中断され、call()メソッドの後続の部分も実行されません.
task.cancel()パラメータをfalseに設定した場合の実行結果
num=1
num=2
task is canceled!
default value
after task.get()!
num=3

task.cancel()パラメータがtrueの場合taskを終了する.get()メソッドのブロックですが、スレッドcall()メソッドの後続の部分は実行され続けます.
Runnableタスクの中断
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        for(int i=0;i<3;i++){
            try {
                Thread.sleep(100);
            }catch (InterruptedException e){
                System.out.println("task is cancel!");
            }
            System.out.println("num="+(i+1));
        }
    }
}
import java.util.concurrent.FutureTask;

public class Test {
    public static void main(String[] agrs){
        String str = "default value1";
        FutureTask task = new FutureTask(new MyRunnable(),str);
        new Thread(task).start();
        try{
            Thread.sleep(250);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        task.cancel(false);
        System.out.println(getFutureValue(task));
        System.out.println("after task.get()!");
    }

    public static String getFutureValue(FutureTask task){
        String str = "default value";
        try{
            str = task.get();
        }catch (Exception e){
            System.out.println("task is canceled!");
        }
        return str;
    }
}

task.cancel(false)の実行結果:
num=1
num=2
task is canceled!
default value
after task.get()!
num=3

task.cancel(true)の実行結果
num=1
num=2
task is cancel!
num=3
task is canceled!
default value
after task.get()!

taskが見えます.cancel()パラメータをtrueにしてもfalseにしてもスレッドは実行されませんが、trueにすると実行結果が「task id cancel!」と多くなり、説明をtrueにするとrun()メソッドは確かに割り込み異常を生じた.Runnableを確実に中断してrun()メソッドを中断後に実行しないようにする場合は、run()メソッドを次のように変更します(すなわち、中断異常が発生した場合、run()メソッドをすぐに返します).
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        for(int i=0;i<3;i++){
            try {
                Thread.sleep(100);
            }catch (InterruptedException e){
                System.out.println("task is cancel!");
                return;
            }
            System.out.println("num="+(i+1));
        }
    }
}


この場合の直接的な結果:
num=1
num=2
task is cancel!
task is canceled!
default value
after task.get()!

4、スレッドプール管理スレッドタスク
ExecutorServiceでは、4つの方法でスレッドタスクを実行できます.このうち3つはRunnableタスクを実行します.
void execute(Runnable command); 
< T > Future< T >  submit(Runnable task, T result); 
Future< ? >  submit(Runnable task);

1個の実行Callableタスク:
< T > Future< T > submit(Callable< T > task); //  Callable  

submitメソッドで実行されるスレッドタスクのみがFutureを取得し、Futureでスレッドを管理できます.サンプルコードは次のとおりです.
import java.util.concurrent.Callable;

public class MyCallable implements Callable {
    @Override
    public String call() throws Exception{
        int num=0;
        for(int i=0;i<3;i++){
            num++;
            Thread.sleep(100);
            System.out.println("num="+num);
        }
        return String.valueOf(num);
    }
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class Test {
    public static void main(String[] agrs){
        ExecutorService service = Executors.newFixedThreadPool(2);
        Future future = service.submit(new MyCallable()); //   submit()  Callable  
        System.out.println("    :"+getFutureValue(future));
        System.out.println("after task.get()!");
        service.shutdown();
    }

    public static String getFutureValue(Future task){
        String str = "default value";
        try{
            str = task.get();
        }catch (Exception e){
            System.out.println("task is canceled!");
        }
        return str;
    }
}

実行結果
num=1
num=2
num=3
    :3
after task.get()!

Callableインタフェースの最初のインスタンスと一致し、実行結果をFutureで取得できます.
5、バックグラウンドスレッドによるタスク管理
ThreadクラスはsetDaemonメソッドを提供し、スレッドをバックグラウンド(デーモン)スレッドに設定し、ホストスレッドタスクが完了するとバックグラウンドスレッドが自動的に終了します.直接コードインスタンス:
public class TestThead implements Runnable {
    private void printNum(int num){
        try{
            Thread.sleep(100);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        System.out.println(num);
    }

    @Override
    public void run() {
        Thread deamon = new Thread(()->{
            for(int i=5;i<10;i++){
                printNum(i);
            }
        });
        deamon.setDaemon(true);
        deamon.start();
        for(int i=0;i<2;i++){
            printNum(i);
        }
    }

    public static void main(String[] args){
        new Thread(new TestThead()).start();
    }
}

実行結果:
5
0
6
1

バックグラウンドスレッドは5,6しか印刷されておらず,メインスレッドのループ実行が完了して終了するとバックグラウンドスレッドも終了し,バックグラウンドスレッドがメインスレッドに従って終了できる機能を検証した.