Detaillierte Erläuterung der Methoden zur Thread-Pool-Erstellung (7 Typen)

1. Erstellung eines Thread-Pools

Es gibt 7 Methoden zum Erstellen von Thread-Pools, diese können jedoch im Allgemeinen in 2 Kategorien unterteilt werden:

1. 通过 ThreadPoolExecutor 创建的线程池;
2. 通过 Executors 创建的线程池。

Es gibt insgesamt 7 Möglichkeiten, einen Thread-Pool zu erstellen (davon werden 6 über Executors und 1 über ThreadPoolExecutor erstellt):

1. Executors.newFixedThreadPool:创建⼀个固定⼤⼩的线程池,可控制并发的线程数,超出的线程会在队列中等待;
2. Executors.newCachedThreadPool:创建⼀个可缓存的线程池,若线程数超过处理所需,缓存⼀段时间后会回收,若线程数不够,则新建线程;
3. Executors.newSingleThreadExecutor:创建单个线程数的线程池,它可以保证先进先出的执⾏顺序;
4. Executors.newScheduledThreadPool:创建⼀个可以执⾏延迟任务的线程池;
5. Executors.newSingleThreadScheduledExecutor:创建⼀个单线程的可以执⾏延迟任务的线程池;
6. Executors.newWorkStealingPool:创建⼀个抢占式执⾏的线程池(任务执⾏顺序不确定)【JDK1.8 添加】。
7. ThreadPoolExecutor:最原始的创建线程池的⽅式,它包含了 7 个参数可供设置,后⾯会详细讲。

2 Eine feste Anzahl von Thread-Pools (newFixedThreadPool)

public static class FixedThreadPool {
    
    
    public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    
        ExecutorService es = Executors.newFixedThreadPool(2);
        // 方式1
        Future<String> submit = es.submit(() -> Thread.currentThread().getName());
        System.out.println(submit.get());
        // 方式2
        es.execute(() -> System.out.println(Thread.currentThread().getName()));
    }
}
输出:
pool-1-thread-1
pool-1-thread-2

2.1 Der Unterschied zwischen Ausführen und Senden

  • Sowohl Execute als auch Submit gehören zur Methode des Thread-Pools, und Execute kann nur Aufgaben vom Typ Runnable senden
  • „submit“ kann sowohl Aufgaben vom Typ „Ausführbar“ als auch vom Typ „Aufrufbar“ senden.
  • execute() hat keinen Rückgabewert
  • „submit“ hat einen Rückgabewert, daher müssen Sie „submit“ verwenden, wenn Sie einen Rückgabewert benötigen
使用submit可以执行有返回值的任务或者是无返回值的任务;而execute只能执行不带返回值的任务。 

2.2 Passen Sie den Namen oder die Priorität des Thread-Pools an

/**
     * 定义线程池名称或优先级
     */
public static class FixedThreadPoll {
    
    
    public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    
        // 创建线程工厂
        ThreadFactory threadFactory = r -> {
    
    
            // !!!!!!!一定要注意:要把任务Runnable设置给新创建的线程
            Thread thread = new Thread(r);
            // 设置线程的命名规则
            thread.setName("我的线程:" + r.hashCode());
            // 设置线程的优先级
            thread.setPriority(Thread.MAX_PRIORITY);
            return thread;
        };
        ExecutorService es = Executors.newFixedThreadPool(2, threadFactory);
        // 参数是接口类,所以lambda的()->{}在括号里面,上面的r->{}是直接一个类,并不是作为入参
        Future<Integer> result = es.submit(() -> {
    
    
            int num = new Random().nextInt(10);
            System.out.println("线程优先级" + Thread.currentThread().getPriority() + ", 随机数:" + num);
            return num;
        });
        // 打印线程池返回结果
        System.out.println("返回结果:" + result.get());
    }
}
输出:
线程优先级10, 随机数:0
返回结果:0
提供的功能:

        1. 设置(线程池中)线程的命名规则。

        2. 设置线程的优先级。

        3. 设置线程分组。

        4. 设置线程类型(用户线程、守护线程)。

3-Thread-Pool mit Cache (newCachedThreadPool)

public static class CachedThreadPool1 {
    
    
    public static void main(String[] args) {
    
    
        // 创建线程池
        ExecutorService es = Executors.newCachedThreadPool();
        for (int i = 0; i < 3; i++) {
    
    
            int finalI = i;
            es.submit(() -> System.out.println("i : " + finalI + "|线程名称:" + Thread.currentThread().getName()));
        }
    }
}
输出:
i : 1|线程名称:pool-1-thread-2
i : 2|线程名称:pool-1-thread-3
i : 0|线程名称:pool-1-thread-1    
优点:线程池会根据任务数量创建线程池,并且在一定时间内可以重复使用这些线程,产生相应的线程池。

缺点:适用于短时间有大量任务的场景,它的缺点是可能会占用很多的资源。

4 Single-Thread-Thread-Pool (newSingleThreadExecutor)

public static class SingleThreadExecutor1 {
    
    
    public static void main(String[] args) {
    
    
        ExecutorService es = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 5; i++) {
    
    
            es.submit(() -> System.out.println("线程名:" + Thread.currentThread().getName()));
        }
    }
}
输出:
线程名:pool-1-thread-1
线程名:pool-1-thread-1
线程名:pool-1-thread-1
线程名:pool-1-thread-1
线程名:pool-1-thread-1    
单线程的线程池又什么意义?
1. 复用线程。
2. 单线程的线程池提供了任务队列和拒绝策略(当任务队列满了之后(Integer.MAX_VALUE),新来的任务就会拒绝策略)

5 Threads, die geplante Aufgaben ausführen (newScheduledThreadPool)

5.1 Verzögerte Ausführung (einmalig)

public static class ScheduledThreadPool1 {
    
    
    public static void main(String[] args) {
    
    
        ScheduledExecutorService ses = Executors.newScheduledThreadPool(3);
        System.out.println("添加任务的时间:" + LocalDateTime.now());
        ses.schedule(() -> System.out.println("执行子任务:" + LocalDateTime.now()), 3, TimeUnit.SECONDS);
    }
}
输出:
添加任务的时间:2022-06-13T18:57:19.742726900
执行子任务:2022-06-13T18:57:22.758147900    

5.2 Ausführung mit fester Frequenz

public static void main(String[] args) {
    
    
    ScheduledExecutorService ses = Executors.newScheduledThreadPool(3);
    System.out.println("添加任务的时间:" + LocalDateTime.now());
    ses.scheduleAtFixedRate(() -> System.out.println(Thread.currentThread().getName() + "执行任务:" + LocalDateTime.now()), 2, 4, TimeUnit.SECONDS);
    // 2是第一次多久之后执行,4是定时任务每隔多久执行一次
}
输出:
添加任务的时间:2022-06-13T19:08:21.725930300
pool-1-thread-1执行任务:2022-06-13T19:08:23.741930800
pool-1-thread-1执行任务:2022-06-13T19:08:27.736881200
pool-1-thread-2执行任务:2022-06-13T19:08:31.742574400
pool-1-thread-2执行任务:2022-06-13T19:08:35.743332900    
如果在定义定时任务中设置了内部睡眠时间,比如
    ses.scheduleAtFixedRate((Runnable) () -> {
    
    
        System.out.println("执行任务: " + LocalDateTime.now());
        try {
    
    
            Thread.sleep(5 * 1000);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
    },2,4, TimeUnit.SECONDS);   
5 > 4 ,哪个值大以哪个值作为定时任务的执行周期

5.3 ScheduleAtFixedRate VS ScheduleWithFixedDelay

public static class ScheduledThreadPool3 {
    
    
    public static void main(String[] args) {
    
    
        // 创建线程池
        ScheduledExecutorService ses = Executors.newScheduledThreadPool(3);
        System.out.println("添加任务时间:" + LocalDateTime.now());
        // 2s之后开始执行定时任务,定时任务每隔4s执行一次
        ses.scheduleWithFixedDelay(() -> {
    
    
            System.out.println(Thread.currentThread().getName() + "执行任务:" + LocalDateTime.now());
            try {
    
    
                Thread.sleep(1000);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        }, 2, 4, TimeUnit.SECONDS);
    }
}
输出:
添加任务时间:2022-06-13T19:20:34.311246
pool-1-thread-1执行任务:2022-06-13T19:20:36.317013100
pool-1-thread-1执行任务:2022-06-13T19:20:41.340114100
pool-1-thread-2执行任务:2022-06-13T19:20:46.362309400
pool-1-thread-2执行任务:2022-06-13T19:20:51.369756400    
scheduleAtFixedRate 是以上⼀次任务的开始时间,作为下次定时任务的参考时间的
scheduleWithFixedDelay 是以上⼀次任务的结束时间,作为下次定时任务的参考时间的。

6 Einzelner Thread für geplante Aufgaben (newSingleThreadScheduledExecutor)

public static class SingleThreadScheduledExecutor1 {
    
    
    public static void main(String[] args) {
    
    
        ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor();
        System.out.println("添加任务时间:" + LocalDateTime.now());
        ses.scheduleAtFixedRate(() -> System.out.println(Thread.currentThread().getName() + "执行时间:" + LocalDateTime.now()), 2, 4, TimeUnit.SECONDS);
    }
}
输出:
添加任务时间:2022-06-13T19:27:06.143427900
pool-1-thread-1执行时间:2022-06-13T19:27:08.146768100
pool-1-thread-1执行时间:2022-06-13T19:27:12.156625
pool-1-thread-1执行时间:2022-06-13T19:27:16.158957500
pool-1-thread-1执行时间:2022-06-13T19:27:20.156330800    

7 Generieren Sie einen Thread-Pool basierend auf der aktuellen CPU (newWorkStealingPool).

public static class WorkStealingPool1 {
    
    
    public static void main(String[] args) {
    
    
        ExecutorService service = Executors.newWorkStealingPool();
        for (int i = 0; i < 5; i++) {
    
    
            service.submit(() -> {
    
    
                System.out.println("线程名:" + Thread.currentThread().getName());
            });
            while (!service.isTerminated()) {
    
    
            }
        }
    }
}
输出:
线程名:ForkJoinPool-1-worker-1    

8 ThreadPoolExecutor

ThreadPoolExecutor erbt von AbstractExecutorService und AbstractExecutorService implementiert die ExecutorService-Schnittstelle

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)
  • corePoolSize: die maximale Anzahl von Kernthreads im Thread-Pool
  • MaximumPoolSize: Der Thread-Pool kann die maximale Anzahl an Threads haben
  • workQueue: eine blockierende Warteschlange zum Zwischenspeichern von Aufgaben

Beim Aufrufen der Thread-Pool-Methode „execute()“ zum Hinzufügen einer Aufgabe trifft der Thread-Pool die folgenden Urteile:

1. Wenn ein Leerlauf-Thread vorhanden ist, führen Sie die Aufgabe direkt aus.
2. Wenn kein Leerlauf-Thread vorhanden ist und die Anzahl der aktuell ausgeführten Threads kleiner als corePoolSize ist, erstellen Sie einen neuen Thread, um die Aufgabe auszuführen.
3. Wenn kein Thread vorhanden ist Leerlauf-Thread und der aktuelle Thread Wenn die Zahl gleich corePoolSize ist und die Blockierungswarteschlange nicht voll ist, wird die Aufgabe in die Warteschlange gestellt, ohne einen neuen Thread hinzuzufügen. 4. Wenn keine
Leerlauf-Threads vorhanden sind und die Blockierungswarteschlange voll ist, wird die Aufgabe in die Warteschlange gestellt Die Anzahl der Threads im Pool ist kleiner als die maximale Poolgröße, ein neuer Thread wird erstellt. Führen Sie die Aufgabe aus.
5. Wenn kein inaktiver Thread vorhanden ist, die Blockierungswarteschlange voll ist und die Anzahl der Threads im Pool gleich der maximalen Poolgröße ist, Lehnen Sie dann neue Aufgaben gemäß der vom Handler im Konstruktor angegebenen Strategie ab.

1) Begrenzte Warteschlange:

SynchronousQueue: Eine blockierende Warteschlange, die keine Elemente speichert. Jeder Einfügevorgang muss warten, bis ein anderer Thread den Entfernungsvorgang aufruft, andernfalls wird der Einfügevorgang immer blockiert. Der Durchsatz ist normalerweise höher als bei LinkedBlockingQueue. Die statische Factory-Methode Executors.newCachedThreadPool verwendet diese Warteschlange .
ArrayBlockingQueue: Eine begrenzte Blockierungswarteschlange, die von einem Array unterstützt wird. Diese Warteschlange sortiert die Elemente nach dem FIFO-Prinzip (First In First Out). Sobald ein solcher Cache erstellt wurde, kann seine Kapazität nicht mehr erhöht werden. Der Versuch, ein Element in eine volle Warteschlange zu stellen, führt zu einer Blockierung des Vorgangs; der Versuch, ein Element aus einer leeren Warteschlange zu extrahieren, führt ebenfalls zu einer Blockierung.
2) Unbegrenzte Warteschlange:

LinkedBlockingQueue: Eine unbegrenzte Blockierungswarteschlange, die auf einer verknüpften Listenstruktur basiert und die Kapazität angeben kann oder nicht (tatsächlich hat jede Warteschlange/jeder Stapel mit unbegrenzter Kapazität Kapazität, und diese Kapazität ist Integer.MAX_VALUE). PriorityBlockingQueue: Es handelt sich um einen
Prioritätsblock Blockierungswarteschlange mit interner Elementreihenfolge. Die Elemente in der Warteschlange müssen die Schnittstelle Comparable implementieren, damit sie durch Implementierung der Methode CompareTo() sortiert werden können. Das Element mit der höchsten Priorität steht immer an der Spitze der Warteschlange; PriorityBlockingQueue garantiert nicht die Reihenfolge von Elementen mit derselben Priorität.

Link: Verwendung des Thread-Pools (7 Erstellungsmethoden) Detaillierte Erläuterung von ThreadPoolExecutor

Supongo que te gusta

Origin blog.csdn.net/qq_41821963/article/details/125341789
Recomendado
Clasificación