メニュー

Java SE11 Gold 勉強すべき内容まとめ【並列処理】#1


本記事では、『Oracle Certified Java Programmer, Gold SE 11』の出題範囲、『並列処理』について、黒本の内容を自分なりに解釈し、まとめていきます

内容が長くなってしまったため、二つに記事を分けています

対象者としては、以下の方を想定しています

  • Java SE11 Goldの試験を受ける予定で、クラスとインターフェースの項目の理解を深めたい方
  • Java SE11 Goldの範囲内で、クラスとインターフェースについて、おさらいしたい方

『Java SE8 Gold』の情報が多い中、『Java SE11 Gold』の内容があまり存在しないため、本記事を作成いたしました

また、このような記事を書いているものの、『Java SE11 Gold』取得に向けて勉強している身のため、その点はご容赦ください

目次

並列処理について

並列処理』では、複数の処理を同時並行で処理することができる

マルチスレッドで処理を行うには『java.lang.Threadクラス』のサブクラスを実装する必要があり、

「runメソッド」に行いたい処理を記述し、「start」メソッドを実行する

public class Hoge {
    public static void main(String[] args) {
        Thread thread = new Main2().new Fuga();
        thread.start();

        System.out.println("Hoge");
    }
	
    public class Fuga extends Thread {
        @Override
        public void run() {
        System.out.println("Fuga");
        }
    }
}
Hoge
Fuga

Runnableインターフェース

新しいスレッドの作成は、『java.lang.Runnableインターフェース』を実現したクラスを、Threadクラスのコンストラクタに渡すことでも実現できる

public class Hoge {
    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("Fuga");
            }
        });
        thread.start();
        System.out.println("Hoge");
}
Hoge
Fuga

Runnableはラムダ式でも記載可能

ExecutorServiceインターフェース

先述のマルチスレッドプログラミングでは、実行したい処理の数だけ新しいスレッドを作成する必要があり、数が多くなればなるほど、スレッドの生成中に最初のスレッドの処理が終了している場合がある

『スレッドプール』では、複数個のスレッドを作成しておき、そのスレッドにタスクを与えて実行させスレッドを使いまわすため、スレッドの無駄遣いが無くなる

使用するにはjava.util.concurrent.ExecutorServiceまたは、java.util.concurrent.ScheduledExecuterServiceを利用する

これらの実装を取得するのが『java.util.concurrent.Executorsクラス』で、「ExecutorServiceインターフェース」を実現したインスタンスへの参照を戻す、以下のメソッドを持っている

メソッド名概要
newSingleThreadExecutor新しいスレッドを一つだけ作成してプールする
newFixedThreadExecutor生成したいスレッド数を引数で受け取り、指定した数のスレッドを保持するスレッドプールを作成する
newCachedThreadPool一度生成されたスレッドは60秒間使用されなければ破棄される
newSingleThreadScheduledExecutor新しいスレッドを一つだけ作成してプールし、メソッドを定期実行する

newSingleThreadExecutor

スレッドIDを確認すると1つのスレッドを使いまわしているのがわかる

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Hoge {
    public static void main(String[] args) {
        ExecutorService single = Executors.newSingleThreadExecutor();
        for(int i = 0; i < 5; i++) {
            single.submit(() -> {
                System.out.println(Thread.currentThread().getId());
        });
		}
}
15
15
15
15
15

newFixedThreadPool

スレッドIDを確認すると引数で渡した5つのスレッドを使いまわしているのがわかる

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Hoge {
    public static void main(String[] args) {
        ExecutorService fixed = Executors.newFixedThreadPool(5);
        for(int i = 0; i < 5; i++) {
            fixed.submit(() -> {
                System.out.println(Thread.currentThread().getId());
        });
		}
}
19
17
16
15
18

newCachedThreadPool

5秒後の確認ではスレッドが使いまわされているが、65秒後の確認では新しいスレッドが生成されているのがわかる

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Hoge {
    public static void main(String[] args) throws Exception {
        ExecutorService cached= Executors.newCachedThreadPool();
        for(int i = 0; i < 5; i++) {
            cached.submit(() -> {
                System.out.println(Thread.currentThread().getId());
            });
        }
		
        Thread.sleep(1 * 5000);
        System.out.println("5秒後");

        for(int i = 0; i < 5; i++) {
            cached.submit(() -> {
                System.out.println(Thread.currentThread().getId());
            });
        }
		
        Thread.sleep(1 * 65000);
        System.out.println("65秒後");
		
        for(int i = 0; i < 5; i++) {
            cached.submit(() -> {
                System.out.println(Thread.currentThread().getId());
            });
        }
}
17
18
15
16
19
5秒後
18
17
16
19
15
65秒後
20
21
21
22
23

ScheduledExecutorServiceインターフェース

java.util.concurrent.ScheduledExecutorService』はjava.util.concurrent.ExecutorServiceを拡張したインターフェースで、メソッドを定期的に実行するためのもの

ScheduledExecutorService』にはいくつかのメソッドが存在する

メソッド名概要
schedule指定した時間遅延させた後、指定した処理を1回実行する
scheduleAtFixedRate初期の遅延時間とインターバル時間の二つを指定し、指定した処理を繰り返し実行する
インターバルよりも処理の時間がかかった場合は、その処理の終了後即時で実行される
scheduleWithFixedDelay初期の遅延時間とインターバル時間の二つを指定し、指定した処理を繰り返し実行する
処理の時間は関係なく、実行終了後インターバルの時間が確保される

scheduleメソッド

scheduleメソッドの引数には、それぞれ以下を指定する

  1. java.util.Runnable型の実行したい処理
  2. long型の遅延させる時間
  3. 遅延させる時間の単位(「java.util.concurrent.TimeUnit」という列挙型を使用する。「HOURS」, 「MINUTES」, 「SECONDS」などが用意されている)

以下は1秒後に実行した例

import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;

public class Hoge {
    public static void main(String[] args) throws Exception {
        ScheduledExecutorService exe = Executors.newSingleThreadScheduledExecutor();
		
        exe.schedule(() -> {
            System.out.println("hoge");
            exe.shutdown(); // スレッドプールを閉じる
        }, 1, TimeUnit.SECONDS);
		
        for (int i = 0; i <= 11; i++) {
            System.out.println(i * 100 + "ms");
            Thread.sleep(100);
        }
		}
}
0ms
100ms
200ms
300ms
400ms
500ms
600ms
700ms
800ms
900ms
hoge
1000ms
1100ms

scheduleAtFixedRateメソッド

scheduleAtFixedRateメソッドの引数には、それぞれ以下を指定する

  1. java.util.Runnable型の実行したい処理
  2. long型の遅延させる時間(初期)
  3. long型の遅延させる時間(インターバル)
  4. 遅延させる時間の単位(「java.util.concurrent.TimeUnit」という列挙型を使用する。「HOURS」, 「MINUTES」, 「SECONDS」などが用意されている)

以下は処理時間を1000ミリ秒までのランダムにし、1秒のインターバルで実行した例

乱数の表示が一定じゃないため、インターバルの時間が確保されていないのがわかる

import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;


public class Hoge {
    public static void main(String[] args) throws Exception {
        ScheduledExecutorService exe = Executors.newSingleThreadScheduledExecutor();
		
        exe.scheduleAtFixedRate(() -> {
            int r = new Random().nextInt(10);
            System.out.print(r);
            try { 
                Thread.sleep(r * 100);
            } catch (InterruptedException e) {
                // 処理なし
            }
            System.out.println("hoge");
		    }, 1, 1, TimeUnit.SECONDS);
		
		        for (int i = 0; i <= 100; i++) {
                System.out.print(">");
                Thread.sleep(100);
		        }
		        exe.shutdown();
		}
}
>>>>>>>>>>9>>>>>>>>hoge
>9>>>>>>>>hoge
>2>>hoge
>>>>>>>3>>>hoge
>>>>>>5>>>>>hoge
>>>>9>>>>>>>>hoge
>7>>>>>>>hoge
>>6>>>>>>hoge
>>>>5>>>>hoge
>>>>>4>>>hoge
>>>>>>8hoge

scheduleWithFixedDelayメソッド

scheduleWithFixedDelayメソッドの引数には、scheduleAtFixedRateメソッド同様それぞれ以下を指定する

  1. java.util.Runnable型の実行したい処理
  2. long型の遅延させる時間(初期)
  3. long型の遅延させる時間(インターバル)
  4. 遅延させる時間の単位(「java.util.concurrent.TimeUnit」という列挙型を使用する。「HOURS」, 「MINUTES」, 「SECONDS」などが用意されている)

以下は処理時間を1000ミリ秒までのランダムにし、1秒のインターバルで実行した例

乱数の表示が一定であるため、インターバルの時間が確保されているのがわかる

import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;

public class Hoge {
    public static void main(String[] args) throws Exception {
        ScheduledExecutorService exe = Executors.newSingleThreadScheduledExecutor();
		
        exe.scheduleWithFixedDelay(() -> {
            int r = new Random().nextInt(10);
            System.out.print(r);
            try { 
                Thread.sleep(r * 100);
            } catch (InterruptedException e) {
                // 処理なし
            }
            System.out.println("hoge");
		    }, 1, 1, TimeUnit.SECONDS);
		
		        for (int i = 0; i <= 100; i++) {
                System.out.print(">");
                Thread.sleep(100);
		        }
		        exe.shutdown();
		}
}
>>>>>>>>>>1>hoge
>>>>>>>>>9>>>>>>>>hoge
>>>>>>>>>9>>>>>>>>>hoge
>>>>>>>>>3>>>hoge
>>>>>>>>>7>>>>>>hoge
>>>>>>>>>9>>>>>>>>>hoge
>>>>>>>>>3>hoge

スレッドプール

ScheduledExecutorService』は『newScheduledThreadPoolメソッド』を使用することで、スレッドプールとして扱うことができる

以下は3つのスレッドが1秒ずつ遅延しながら実行されている例

import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;

public class Hoge {
    public static void main(String[] args) {
		    ScheduledExecutorService exe = Executors.newScheduledThreadPool(3);
		
		    exe.scheduleWithFixedDelay(() -> {
		        System.out.print("1");
		    }, 1, 1, TimeUnit.SECONDS);
		
		    exe.scheduleWithFixedDelay(() -> {
		        System.out.print("2");
		    }, 1, 1, TimeUnit.SECONDS);
		
		    exe.scheduleWithFixedDelay(() -> {
		        System.out.print("3");
		    }, 1, 1, TimeUnit.SECONDS);
		
		    Thread.sleep(10000);
		    exe.shutdown();
    }
}
321321321312312312321312321

Futureインターフェース

Futureインターフェースを使用することで、スレッドを外部から操作することができる

submitメソッド引数無

スレッドの処理が完了すると『null』を返す

import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;

public class Hoge {
    public static void main(String[] args) {
        ExecutorService exe = Executors.newSingleThreadExecutor();
        Future future = exe.submit(() -> {
            System.out.println("fuga");
        });
		
        if (future.get() == null) {
            System.out.println("hoge");
        }
    }
}
fuga
hoge

submitメソッド引数有

『null』以外の値を戻したい場合は、『submitメソッド』の第二引数に戻したい値を指定する

import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;

public class Hoge {
    public static void main(String[] args) {
        ExecutorService exe = Executors.newSingleThreadExecutor();
        Future<String> future = exe.submit(() -> {
            System.out.println("fuga");
        }, "hoge");
		
        System.out.println(future.get());
    }
}
fuga
hoge

Callableインターフェース

「Futureインターフェース」の「getメソッド」では、「null」または、「固定の値」しか戻せない

それに対し、『Callableインターフェース』では、処理結果を戻したり、例外をスローするタスクを定義できる

処理結果を戻す例

1 + 1の結果を戻している

import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;

public class Hoge {
    public static void main(String[] args) {
        ExecutorService exe = Executors.newSingleThreadExecutor();
        Callable<Integer> task = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                return 1 + 1;
            }
        };
		
        Future future = exe.submit(task);
        System.out.println(future.get());
    }
}
2

例外をスローする例

import java.util.Random
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;

public class Hoge {
    public static void main(String[] args) {
        ExecutorService exe = Executors.newSingleThreadExecutor();
        Callable<Integer> task = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                if (new Random().nextInt(1) != 10) {
                    throw new Exception("fuga");
                }
                return 1 + 1;
            }
        };
		
        Future future = exe.submit(task);
		
        try {
            System.out.println(future.get());
        } catch (ExecutionException e) {
            System.out.println(e.getMessage());
        }
    }
}
fuga

おわりに

自分のアウトプットも兼ねて、Java SE11 Goldの試験で出題される、並列処理についてまとめました

間違っている点があった場合は、教えてくださると助かります

他の章についてもまとめていきますので、お待ちいただければと思います

created by Rinker
¥4,400 (2023/04/01 00:34:43時点 楽天市場調べ-詳細)
  • URLをコピーしました!

書いた人

1K6畳住み一人暮らし
快適な部屋にするために日々邁進中

目次
閉じる