[Thread] 7 Möglichkeiten zum Erstellen von Thread-Pools und zum Anpassen von Thread-Pools

1. Was ist ein Thread-Pool?

Thread-Pool (ThreadPool) ist ein Mechanismus zum Verwalten und Verwenden von Threads, der auf der Idee des Poolings basiert: Er speichert mehrere Threads im Voraus in einem „Pool“ und kann beim Erscheinen einer Aufgabe die durch die Neuerstellung verursachte Leistung vermeiden und Zerstören von Threads. Overhead, Sie müssen nur den entsprechenden Thread aus dem „Pool“ herausnehmen, um die entsprechende Aufgabe auszuführen.

Die Verwendung eines Thread-Pools bietet die folgenden Hauptvorteile:

  1. Reduzieren Sie den Ressourcenverbrauch ( verwenden Sie Threads wieder und reduzieren Sie den Overhead, der durch häufiges Erstellen und Zerstören von Threads verursacht wird ) .
  2. Verbessern Sie die Reaktionsgeschwindigkeit
  3. Verbessern Sie die Thread-Verwaltbarkeit

gleichzeitig,Alibaba schreibt in seinem „Java Development Manual“ außerdem vor, dass Thread-Ressourcen über den Thread-Pool bereitgestellt werden müssen und die explizite Erstellung von Threads in der Anwendung nicht zulässig ist.

2. Thread-Pool verwenden

Es gibt insgesamt 7 Möglichkeiten, einen Thread-Pool zu erstellen, aber im Allgemeinen können sie in 2 Kategorien unterteilt werden:

  • ThreadPoolExecutorThread-Pool erstellt von
  • ExecutorsThread-Pool erstellt von

Es gibt insgesamt 7 Möglichkeiten, einen Thread-Pool zu erstellen (6 davon werden Executorsvon erstellt und 1 wird ThreadPoolExecutorvon erstellt):

  1. Executors.newFixedThreadPool(): Erstellen Sie einen Thread-Pool mit fester Größe, der die Anzahl gleichzeitiger Threads steuern kann. Überzählige Threads warten in der Warteschlange.
  2. Executors.newCachedThreadPool(): Erstellen Sie einen zwischenspeicherbaren Thread-Pool. Wenn die Anzahl der Threads die Verarbeitungsanforderungen überschreitet, werden die überschüssigen Threads nach einer gewissen Zeit zwischengespeichert. Wenn die Anzahl der Threads nicht ausreicht, werden neue Threads erstellt.
  3. Executors.newSingleThreadExecutor(): Erstellen Sie einen Thread-Pool mit einer einzigen Anzahl von Threads, der die Ausführungsreihenfolge „First In, First Out“ garantieren kann.
  4. Executors.newScheduledThreadPool():Erstellen Sie einen Thread-Pool, der verzögerte Aufgaben ausführen kann.
  5. Executors.newSingleThreadScheduledExecutor(): Erstellen Sie einen Single-Thread-Thread-Pool, der verzögerte Aufgaben ausführen kann.
  6. Executors.newWorkStealingPool(): Erstellen Sie einen Thread-Pool für die präventive Ausführung (die Reihenfolge der Aufgabenausführung ist ungewiss). JDK 1.8 hinzugefügt
  7. ThreadPoolExecutor: Die originellste Art, einen Thread-Pool zu erstellen. Er enthält 7 Parameter zum Festlegen

Die Bedeutung von Single-Thread-Pools: Obwohl newSingleThreadExecutores sich newSingleThreadScheduledExecutorum Single-Thread-Pools handelt, bieten sie Funktionen wie Arbeitswarteschlangen, Lebenszyklusverwaltung und Worker-Thread-Wartung.

2.1 ThreadPoolExecutor

Schauen wir uns zunächst die originellste Methode zum Erstellen eines Thread-Pools an ThreadPoolExecutor:

public class ThreadPoolExecutorTest {
    
    

    public static void main(String[] args) {
    
    
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 10, 100, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10));
        // 执行任务
        for (int i = 0; i < 10; i++) {
    
    
            final int index = i;
            threadPool.execute(() -> {
    
    
                System.out.println(index + " 被执行,线程名:" + Thread.currentThread().getName());
                try {
    
    
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            });
        }
    }
}

ThreadPoolExecutor kann bis zu 7 Parameter festlegen:

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
     BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
    
    }

Die Bedeutung der 7 Parameter ist wie folgt:

  1. corePoolSize : Die Anzahl der Kernthreads, die Anzahl der Threads, die immer im Thread-Pool aktiv sind.

  2. MaximumPoolSize : Die maximale Anzahl von Threads, die maximal zulässige Anzahl von Threads im Thread-Pool und die maximale Anzahl von Threads, die erstellt werden können, wenn die Aufgabenwarteschlange des Thread-Pools voll ist .

  3. keepAliveTime : Die Zeit, die die maximale Anzahl von Threads überleben kann. Wenn im Thread keine Aufgaben ausgeführt werden, wird ein Teil des maximalen Threads zerstört und schließlich die Anzahl der Kernthreads beibehalten.

  4. Einheit : Die Einheit wird in Verbindung mit Parameter 3 Überlebenszeit verwendet und zusammen werden sie verwendet, um die Überlebenszeit des Threads festzulegen. Die Zeiteinheit des Parameters keepAliveTime hat die folgenden 7 Optionen:

    • TimeUnit.DAYS: Tage
    • TimeUnit.HOURS: Stunden
    • TimeUnit.MINUTES: Minuten
    • TimeUnit.SECONDS: Sekunden
    • TimeUnit.MILLISECONDS: Millisekunden
    • TimeUnit.MICROSECONDS: subtil
    • TimeUnit.NANOSECONDS: Nanosekunden
  5. workQueue : Eine blockierende Warteschlange, in der Aufgaben gespeichert werden, die auf die Ausführung durch den Thread-Pool warten. Sie sind alle Thread-sicher. Es ist im Allgemeinen in direkte Übermittlungswarteschlange, begrenzte Aufgabenwarteschlange, unbegrenzte Aufgabenwarteschlange und Prioritätsaufgabenwarteschlange unterteilt, einschließlich der folgenden sieben Typen:

    • ArrayBlockingQueue: Eine begrenzte Blockierungswarteschlange, die aus einer Array-Struktur besteht.
    • LinkedBlockingQueue: Eine begrenzte Blockierungswarteschlange, die aus einer verknüpften Listenstruktur besteht.
    • SynchronousQueue: Eine blockierende Warteschlange, die keine Elemente speichert, dh sie direkt an den Thread sendet, ohne sie beizubehalten.
    • PriorityBlockingQueue: Eine unbegrenzte Blockierungswarteschlange, die die Prioritätssortierung unterstützt.
    • DelayQueue: Eine unbegrenzte Blockierungswarteschlange, die mithilfe einer Prioritätswarteschlange implementiert wird und aus der Elemente nur extrahiert werden können, wenn die Verzögerung abläuft
    • LinkedTransferQueue: Eine unbegrenzte Blockierungswarteschlange, die aus einer verknüpften Listenstruktur besteht. Ähnlich wie SynchronousQueue enthält es auch nicht blockierende Methoden.
    • LinkedBlockingDeque: Eine bidirektionale Blockierungswarteschlange, die aus einer verknüpften Listenstruktur besteht.

    Die am häufigsten verwendeten sind LinkedBlockingQueueund Synchronous. Die Warteschlangenstrategie des Thread-Pools hängt mit BlockingQueue zusammen

  6. threadFactory : Thread-Factory, die hauptsächlich zum Erstellen von Threads verwendet wird.

  7. Handler : Ablehnungsstrategie, die Strategie zur Ablehnung von Verarbeitungsaufgaben. Das System bietet 4 Optionen:

    • AbortPolicy: Ablehnen und eine Ausnahme auslösen.
    • CallerRunsPolicy: Verwenden Sie den aktuellen aufrufenden Thread, um diese Aufgabe auszuführen.
    • DiscardOldestPolicy: Verwerfen Sie eine Aufgabe an der Spitze der Warteschlange (älteste) und führen Sie die aktuelle Aufgabe aus.
    • DiscardPolicy: Die aktuelle Aufgabe ignorieren und verwerfen.

Die Standardrichtlinie ist AbortPolicy

Thread-Pool-Ausführungsprozess

Der Ausführungsablauf der Schlüsselknoten von ThreadPoolExecutor ist wie folgt:

  1. Threads werden erstellt, wenn die Anzahl der Threads geringer ist als die Anzahl der Kern-Threads.
  2. Wenn die Anzahl der Threads größer oder gleich der Anzahl der Kernthreads ist und die Aufgabenwarteschlange nicht voll ist, wird die Aufgabe in die Aufgabenwarteschlange gestellt.
  3. Wenn die Anzahl der Threads größer oder gleich der Anzahl der Kernthreads ist und die Aufgabenwarteschlange voll ist: Wenn die Anzahl der Threads kleiner als die maximale Anzahl von Threads ist, erstellen Sie einen Thread. Wenn die Anzahl der Threads gleich ist Bei maximaler Anzahl an Threads wird eine Ausnahme ausgelöst und die Aufgabe abgelehnt.

Fügen Sie hier eine Bildbeschreibung ein

2.2 FixedThreadPool

FixedThreadPool: Erstellen Sie einen Thread-Pool mit fester Größe, der die Anzahl gleichzeitiger Threads steuern kann. Überzählige Threads warten in der Warteschlange.

public static ExecutorService newFixedThreadPool(int nThreads) {
    
    
    return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
}
  • corePoolSize ist gleich MaximumPoolSize , das heißt, alle seine Threads sind Kern-Threads, und es handelt sich um einen Thread-Pool mit fester Größe, was seinen Vorteil darstellt;
  • keepAliveTime = 0 Dieser Parameter ist standardmäßig für Kernthreads ungültig, und FixedThreadPoolalle sind Kernthreads.
  • Die WorkQueue ist LinkedBlockingQueue(unbegrenzte Blockierungswarteschlange) und der Maximalwert der Warteschlange beträgt Integer.MAX_VALUE. Wenn die Geschwindigkeit der Aufgabenübermittlung weiterhin höher ist als die Geschwindigkeit der Aufgabenverarbeitung, führt dies zu einer starken Überlastung der Warteschlange. Da die Warteschlange groß ist, ist es sehr wahrscheinlich, dass der Speicher überläuft, bevor die Richtlinie abgelehnt wird. ist sein Nachteil ;
  • FixedThreadPoolDie Aufgabenausführung istStörungvon;

Anwendbare Szenarien: Es kann zum sofortigen Spitzenausgleich von Webdiensten verwendet werden, es muss jedoch auf die Blockierung der Warteschlange durch langfristige Spitzenbedingungen geachtet werden.

public class NewFixedThreadPoolTest {
    
    

    public static void main(String[] args) {
    
    
        System.out.println("主线程启动");
        // 1.创建1个有2个线程的线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(2);

        Runnable runnable = new Runnable() {
    
    
            @Override
            public void run() {
    
    
                try {
    
    
                    Thread.sleep(2000);
                } catch (Exception e) {
    
    
                    e.printStackTrace();
                }
                System.out.println("任务被执行,线程:" + Thread.currentThread().getName());
            }
        };
        // 2.线程池执行任务(添加4个任务,每次执行2个任务,得执行两次)
        threadPool.submit(runnable);
        threadPool.execute(runnable);
        threadPool.execute(runnable);
        threadPool.execute(runnable);
        System.out.println("主线程结束");
    }
}

Der obige Code: Erstellt einen Thread-Pool mit 2 Threads, weist ihm jedoch jeweils 4 Aufgaben zu und kann jeweils nur 2 Aufgaben ausführen, sodass er zweimal ausgeführt werden muss.

Dieser Thread-Pool verwendet eine feste Anzahl von Threads wieder, die in einer gemeinsam genutzten, unbegrenzten Warteschlange ausgeführt werden. Zu jedem Zeitpunkt sind bis zu nThreads-Threads aktive Verarbeitungsaufgaben. Wenn andere Aufgaben übermittelt werden, während alle Threads aktiv sind, warten sie in der Warteschlange, bis ein Thread verfügbar wird. Es werden also jeweils zwei Aufgaben (zwei aktive Threads) ausgeführt, während die anderen beiden Aufgaben in der Arbeitswarteschlange warten.

submit()Methoden und execute()Methoden sind Möglichkeiten, Aufgaben auszuführen. Der Unterschied zwischen ihnen besteht darin, dass submit()Methoden Rückgabewerte haben, während execute()Methoden keine Rückgabewerte haben.

2.3 CachedThreadPool

CachedThreadPool: Erstellen Sie einen zwischenspeicherbaren Thread-Pool. Wenn die Anzahl der Threads die Verarbeitungsanforderungen überschreitet, wird der Cache nach einer gewissen Zeit recycelt. Wenn die Anzahl der Threads nicht ausreicht, wird ein neuer Thread erstellt.

public static ExecutorService newCachedThreadPool() {
    
    
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
}
  • corePoolSize = 0, MaximumPoolSize = Integer.MAX_VALUE Das heißt, die Anzahl der Threads ist nahezu unbegrenzt.
  • keepAliveTime = 60s , der Thread wird automatisch beendet, nachdem er 60 Sekunden lang im Leerlauf war
  • workQueue ist SynchronousQueueeine Synchronisationswarteschlange. Diese Warteschlange ähnelt einem Staffelstab. Der Ein- und Ausstieg muss gleichzeitig erfolgen. Da die CachedThreadPoolThread-Erstellung unbegrenzt ist und keine Warteschlange wartet, verwenden Sie sieSynchronousQueue

Anwendbare Szenarien: Verarbeiten Sie schnell eine große Anzahl kurzfristiger Aufgaben, z. B. NettyNIO, die bei der Annahme von Anfragen verwendet werden können CachedThreadPool.

public class NewCachedThreadPool {
    
    

    public static void main(String[] args) {
    
    
        ExecutorService threadPool = Executors.newCachedThreadPool();

        for (int i = 0; i < 10; i++) {
    
    
            threadPool.execute(() -> {
    
    
                System.out.println("任务被执行,线程:" + Thread.currentThread().getName());
                try {
    
    
                    TimeUnit.SECONDS.sleep(1);
                } catch (Exception e) {
    
    
                    e.printStackTrace();
                }
            });
        }
    }
}

Dem Endergebnis nach zu urteilen, hat der Thread-Pool 10 Threads erstellt, um die entsprechenden Aufgaben auszuführen

2.4 SingleThreadExecutor

SingleThreadExecutor: Erstellen Sie einen Thread-Pool mit einer einzigen Anzahl von Threads, der die Ausführungsreihenfolge „First In, First Out“ garantieren kann.

public class SingleThreadExecutorTest {
    
    

    public static void main(String[] args) {
    
    
        ExecutorService threadPool = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 10; i++) {
    
    
            final int index = i;
            threadPool.execute(() -> {
    
    
                System.out.println(index + ":任务被执行");
                try {
    
    
                    TimeUnit.SECONDS.sleep(1);
                } catch (Exception e) {
    
    
                    e.printStackTrace();
                }
            });
        }
    }
}

Die Ausführungsergebnisse sind wie folgt:
Fügen Sie hier eine Bildbeschreibung ein

2.5 ScheduledThread

ScheduledThreadPool: Erstellen Sie einen Thread-Pool, der verzögerte Aufgaben ausführen kann.

public class ScheduledThreadTest {
    
    

    public static void main(String[] args) {
    
    
        ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(5);
        System.out.println("添加任务,时间:" + new Date());
        threadPool.schedule(() -> {
    
    
            System.out.println("任务被执行,时间:" + new Date());
        }, 2, TimeUnit.SECONDS);
    }
}

Die Ausführungsergebnisse sind wie folgt:
Fügen Sie hier eine Bildbeschreibung ein
Aus den obigen Ergebnissen können wir ersehen, dass die Aufgabe nach 2 Sekunden ausgeführt wurde, was unseren Erwartungen entspricht.

2.6 SingleThreadScheduledExecutor

SingleThreadScheduledExecutor: Erstellen Sie einen Single-Threaded-Thread-Pool, der verzögerte Aufgaben ausführen kann.

public class SingleThreadScheduledExecutorTest {
    
    

    public static void main(String[] args) {
    
    
        ScheduledExecutorService threadPool = Executors.newSingleThreadScheduledExecutor();
        System.out.println("添加任务,时间:" + new Date());
        threadPool.schedule(() -> {
    
    
            System.out.println("任务被执行,时间:" + new Date());
            try {
    
    
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
    
    
            }
        }, 2, TimeUnit.SECONDS);
    }
}

2.7 NewWorkStealingPool

NewWorkStealingPool: Erstellen Sie einen Thread-Pool für die präventive Ausführung (die Reihenfolge der Aufgabenausführung ist ungewiss). Beachten Sie, dass diese Methode nur in der JDK-Version 1.8+ verwendet werden kann

public class NewWorkStealingPoolTest {
    
    

    public static void main(String[] args) {
    
    
        ExecutorService threadPool = Executors.newWorkStealingPool();
        for (int i = 0; i < 10; i++) {
    
    
            final int index = i;
            threadPool.execute(() -> {
    
    
                System.out.println(index + " 被执行,线程名:" + Thread.currentThread().getName());
            });
        }
        // 确保任务执行完成
        while (!threadPool.isTerminated()) {
    
    
        }
    }
}

Dem obigen Code nach zu urteilen, ist die Ausführungsreihenfolge der Aufgaben ungewiss, da sie präventiv ausgeführt wird.

3. Aufgabenwarteschlange

Aufgabenwarteschlange: Im Allgemeinen unterteilt in direkte Übermittlungswarteschlange, begrenzte Aufgabenwarteschlange, unbegrenzte Aufgabenwarteschlange und Prioritätsaufgabenwarteschlange.

1. Senden Sie die Warteschlange direkt : Legen Sie sie alsSynchronousQueueWarteschlange fest. SynchronousQueue ist eine spezielle BlockingQueue. Sie hat keine Kapazität. Jedes Mal, wenn ein Einfügevorgang ausgeführt wird, wird sie blockiert. Sie muss einen weiteren Löschvorgang ausführen, bevor sie aktiviert werden kann. Im Gegenteil, jeder Löschvorgang muss auch ausgeführt werden. Warten Sie auf den entsprechenden Einfügevorgang.

public class SynchronousQueueTest {
    
    

    public static void main(String[] args) {
    
    
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, 2, 100, TimeUnit.SECONDS, new SynchronousQueue<>());
        for (int i = 0; i < 3; i++) {
    
    
            threadPool.execute(() -> System.out.println(Thread.currentThread().getName()));
        }
    }
}

Es ist ersichtlich, dass die Ablehnungsrichtlinie SynchronousQueuedirekt ausgeführt und eine Ausnahme ausgelöst wird, wenn die Aufgabenwarteschlange vorhanden ist und die Anzahl der erstellten Threads größer als MaximumPoolSize ist.

Bei Verwendung von SynchronousQueueWarteschlangen werden übermittelte Aufgaben nicht gespeichert und immer sofort zur Ausführung weitergeleitet. Wenn die Anzahl der zum Ausführen von Aufgaben verwendeten Threads geringer ist als MaximumPoolSize, versuchen Sie, einen neuen Prozess zu erstellen. Wenn der durch MaximumPoolSize festgelegte Maximalwert erreicht ist, wird die Ablehnungsrichtlinie gemäß dem von Ihnen festgelegten Handler ausgeführt. Daher werden die Aufgaben, die Sie auf diese Weise übermitteln, nicht zwischengespeichert, sondern sofort ausgeführt. In diesem Fall müssen Sie die Parallelität Ihres Programms genau einschätzen, um die entsprechende maximale PoolSize-Menge festzulegen, andernfalls wird dies der Fall sein sehr schwierig. Es ist einfach, eine Ablehnungsrichtlinie umzusetzen

2. Begrenzte Aufgabenwarteschlange : Die begrenzte Aufgabenwarteschlange kannArrayBlockingQueueimplementiert

ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, 2, 1000, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(10),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());

Wenn bei Verwendung ArrayBlockingQueueeiner begrenzten Aufgabenwarteschlange neue Aufgaben ausgeführt werden müssen, erstellt der Thread-Pool neue Threads. Wenn die Anzahl der erstellten Threads corePoolSize erreicht, werden die neuen Aufgaben der Warteschlange hinzugefügt. Wenn die Warteschlange voll ist, dh die von ArrayBlockingQueue initialisierte Kapazität überschreitet, werden weiterhin Threads erstellt, bis die Anzahl der Threads die durch MaximumPoolSize festgelegte maximale Anzahl von Threads erreicht. Wenn sie größer als MaximumPoolSize ist, wird die Ablehnungsrichtlinie angewendet ausgeführt werden. In diesem Fall hängt die Obergrenze der Thread-Anzahl direkt vom Status der begrenzten Aufgabenwarteschlange ab. Wenn die anfängliche Kapazität der begrenzten Warteschlange groß ist oder keinen Überlastungszustand erreicht hat, wird die Anzahl der Threads immer beibehalten unter corePoolSize. Andernfalls, wenn die Aufgabenwarteschlange voll ist, wird MaximumPoolSize als Obergrenze für die maximale Anzahl von Threads verwendet.

3. Unbegrenzte Aufgabenwarteschlange : Eine begrenzte Aufgabenwarteschlange kannLinkedBlockingQueueimplementiert

ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, 2, 1000, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());

Bei Verwendung einer unbegrenzten Aufgabenwarteschlange kann die Aufgabenwarteschlange des Thread-Pools unbegrenzt neue Aufgaben hinzufügen, und die maximale Anzahl der vom Thread-Pool erstellten Threads ist die durch Ihre corePoolSize festgelegte Zahl, was bedeutet, dass in diesem Fall der Parameter MaximumPoolSize ungültig ist. Auch wenn in Ihrer Aufgabenwarteschlange viele nicht ausgeführte Aufgaben zwischengespeichert sind. Wenn die Anzahl der Threads im Thread-Pool corePoolSize erreicht, erhöht sie sich nicht mehr. Wenn später neue Aufgaben hinzugefügt werden, werden sie direkt in die Warteschlange gestellt, um zu warten. Wenn Sie dies verwenden Im Aufgabenwarteschlangenmodus müssen Sie auf die Koordination und Kontrolle zwischen der Übermittlung und Verarbeitung Ihrer Aufgabe achten. Andernfalls besteht das Problem, dass die Aufgaben in der Warteschlange weiter wachsen, da sie nicht rechtzeitig verarbeitet werden können, bis die Ressourcen vorhanden sind endlich erschöpft.

4. Strategie zur Thread-Ablehnung

Lassen Sie uns ThreadPoolExecutordie Auslösung der Ablehnungsrichtlinie demonstrieren. Wir verwenden DiscardPolicydie Ablehnungsrichtlinie. Sie ignoriert und verwirft die Richtlinie der aktuellen Aufgabe. Der Implementierungscode lautet wie folgt:

public class ThreadPoolStrategyTest {
    
    

    public static void main(String[] args) {
    
    
        // 线程池
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, 1, 100,
                TimeUnit.SECONDS, new LinkedBlockingQueue<>(1), new ThreadPoolExecutor.DiscardPolicy());
        // 任务
        Runnable runnable = new Runnable() {
    
    
            @Override
            public void run() {
    
    
                System.out.println("当前任务被执行,执行时间:" + new Date() +
                        " 执行线程:" + Thread.currentThread().getName());

                try {
    
    
                    TimeUnit.SECONDS.sleep(1);
                } catch (Exception e) {
    
    
                    e.printStackTrace();
                }
            }
        };
        // 开启4个任务
        threadPool.execute(runnable);
        threadPool.execute(runnable);
        threadPool.execute(runnable);
        threadPool.execute(runnable);
    }
}

Wir haben einen Thread-Pool erstellt, bei dem die Anzahl der Kern-Threads und die maximale Anzahl an Threads beide gleich 1 sind, und die Aufgabenwarteschlange des Thread-Pools auf 1 gesetzt, sodass bei mehr als 2 Aufgaben die Ablehnungsrichtlinie ausgelöst wird. Die Ausführungsergebnisse sind wie folgt:
Fügen Sie hier eine Bildbeschreibung ein
Aus den obigen Ergebnissen ist ersichtlich, dass nur zwei Aufgaben korrekt ausgeführt wurden und andere redundante Aufgaben verworfen und ignoriert wurden. Der Einsatz anderer Ablehnungsstrategien ist ähnlich, daher werde ich hier nicht näher darauf eingehen.

Benutzerdefinierte Ablehnungsrichtlinie

Zusätzlich zu den vier von Java selbst bereitgestellten Ablehnungsstrategien können wir die Ablehnungsstrategie auch anpassen. Der Beispielcode lautet wie folgt:

public class MyThreadPoolStrategyTest {
    
    

    public static void main(String[] args) {
    
    
        Runnable runnable = new Runnable() {
    
    
            @Override
            public void run() {
    
    
                System.out.println("当前任务被执行,执行时间:" + new Date() +
                        " 执行线程:" + Thread.currentThread().getName());

                try {
    
    
                    TimeUnit.SECONDS.sleep(1);
                } catch (Exception e) {
    
    
                    e.printStackTrace();
                }
            }
        };
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, 1, 100,
                TimeUnit.SECONDS, new LinkedBlockingQueue<>(1), new RejectedExecutionHandler() {
    
    
            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
    
    
                // 执行自定义拒绝策略的相关操作
                System.out.println("我是自定义拒绝策略~");
            }
        });

        threadPool.execute(runnable);
        threadPool.execute(runnable);
        threadPool.execute(runnable);
        threadPool.execute(runnable);
    }
}

Die Ausführungsergebnisse des Programms sind wie folgt:

Fügen Sie hier eine Bildbeschreibung ein

Benutzerdefinierter Thread-Pool

Das Folgende ist eine Demo eines benutzerdefinierten Thread-Pools mit begrenzten Warteschlangen, Anpassungs- ThreadFactoryund Ablehnungsrichtlinien:

public class MyThreadPoolTest {
    
    

    public static void main(String[] args) throws Exception {
    
    
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(2);
        NameThreadFactory threadFactory = new NameThreadFactory();
        RejectedExecutionHandler handler = new MyIgnorePolicy();
        ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 4, 100, TimeUnit.SECONDS,
                workQueue, threadFactory, handler);
        // 预启动所有核心线程
        executor.prestartAllCoreThreads();

        for (int i = 1; i <= 10; i++) {
    
    
            MyTask task = new MyTask(String.valueOf(i));
            executor.execute(task);
        }

        //阻塞主线程
        System.in.read();
    }

    static class NameThreadFactory implements ThreadFactory {
    
    
        private final AtomicInteger mThreadNum = new AtomicInteger(1);

        @Override
        public Thread newThread(Runnable r) {
    
    
            Thread t = new Thread(r, "my-thread-" + mThreadNum.getAndIncrement());
            System.out.println(t.getName() + " has been created");
            return t;
        }
    }

    static class MyIgnorePolicy implements RejectedExecutionHandler {
    
    
        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
    
    
            doLog(r, executor);
        }

        private void doLog(Runnable r, ThreadPoolExecutor e) {
    
    
            // 可做日志记录等
            System.err.println( r.toString() + " rejected");
            System.out.println("completedTaskCount: " + e.getCompletedTaskCount());
        }
    }

    static class MyTask implements Runnable {
    
    
        private String name;

        public MyTask(String name) {
    
    
            this.name = name;
        }

        @Override
        public void run() {
    
    
            try {
    
    
                System.out.println(this.toString() + " is running!");
                // 让任务执行慢点
                Thread.sleep(3000);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        }

        public String getName() {
    
    
            return name;
        }

        @Override
        public String toString() {
    
    
            return "MyTask [name=" + name + "]";
        }
    }
}

Fügen Sie hier eine Bildbeschreibung ein
Unter ihnen belegten die Threads 1 bis 4 zunächst die Kernthreads und die maximale Anzahl an Threads, und dann traten die Aufgaben 4, 5 und 6 in die Warteschlange ein. Aufgabe 7 bis 10 und Aufgabe 5 wurden direkt ignoriert und ihre Ausführung verweigert. 4 wurde benachrichtigt, nachdem Threads in den Aufgaben 1–4 ausgeführt wurden. ,5,6 Aufgaben werden weiterhin ausgeführt.

5. Thread-Pool-Toolklasse

public class TaskCenterUtil {
    
    

    public static Integer CORE_POOL_SIZE = 10;
    public static Integer MAX_NUM_POOL_SIZE = 10;
    public static Integer MAX_MESSAGE_SIZE = 100;
    public static Long KEEP_ALIVE_TIME = 60L;

    private final ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE, MAX_NUM_POOL_SIZE, KEEP_ALIVE_TIME,
            TimeUnit.SECONDS, new LinkedBlockingQueue<>(MAX_MESSAGE_SIZE), new CustomerThreadFactory("zzc-thread-pool"), new ThreadPoolExecutor.CallerRunsPolicy());

    static class CustomerThreadFactory implements ThreadFactory {
    
    
        private String name;
        private final AtomicInteger mThreadNum = new AtomicInteger(1);
        public CustomerThreadFactory(String name) {
    
    
            this.name = name;
        }
        @Override
        public Thread newThread(Runnable r) {
    
    
            return new Thread(r, name + + mThreadNum.getAndIncrement());
        }
    }

    private TaskCenterUtil() {
    
    }

    private static final TaskCenterUtil taskCenterUtil = new TaskCenterUtil();

    public static TaskCenterUtil getTaskCenterUtil() {
    
    
        return taskCenterUtil;
    }

    // 提交任务(有返回值)
    public <T> Future<T> submitTask(Callable<T> task) {
    
    
        return poolExecutor.submit(task);
    }

    public void submitTask2(Callable task) {
    
    
        poolExecutor.submit(task);
    }

    // 提交任务(无返回值)
    public void executeTask(Runnable task) {
    
    
        poolExecutor.execute(task);
    }

    public void shutdown() {
    
    
        poolExecutor.shutdown();
    }
}

verwenden:

1. Verwenden Sie Lambda

TaskCenterUtil.getTaskCenterUtil().submitTask2(() -> {
    
    
	log.info("【批量添加】批量添加数据:{}", JSON.toJSONString(excelVos));
    return null;
});

2.Verwenden Sie Klassen

public class DeviceDataTask implements Runnable {
    
    
	@Override
    public void run() {
    
    
        log.info("【批量添加】批量添加数据:{}", JSON.toJSONString(excelVos));
    }
}
TaskCenterUtil taskCenterUtil = TaskCenterUtil.getTaskCenterUtil();
taskCenterUtil.executeTask(new DeviceDataTask());
taskCenterUtil.shutdown();

6. Welchen Thread-Pool soll ich wählen?

Nach der obigen Studie haben wir ein gewisses Verständnis für den gesamten Thread-Pool. Wie wählt man also einen Thread-Pool aus?

Werfen wir einen Blick auf die Antwort, die uns Alibabas „Java Development Manual“ gibt:

[Obligatorisch] Thread-Pools dürfen nicht mit Executoren, sondern mit ThreadPoolExecutor erstellt werden. Diese Verarbeitungsmethode ermöglicht es Schülern, die schreiben, die Betriebsregeln des Thread-Pools klarer zu kennen und das Risiko einer Ressourcenerschöpfung zu vermeiden.

Hinweis: Die von Executors zurückgegebenen Thread-Pool-Objekte haben folgende Nachteile:
1) FixedThreadPool und SingleThreadPool: Die zulässige Länge der Anforderungswarteschlange beträgt Integer.MAX_VALUE, wodurch sich eine große Anzahl von Anforderungen ansammeln kann, was zu OOM führt.
2) CachedThreadPool: Die zulässige Anzahl der zu erstellenden Threads beträgt Integer.MAX_VALUE, wodurch möglicherweise eine große Anzahl von Threads erstellt wird, was zu OOM führt.

Um die obige Situation zusammenzufassen, empfehlen wir daher, ThreadPoolExecutordie Methode zum Erstellen eines Thread-Pools zu verwenden, da diese Erstellungsmethode besser kontrollierbar ist und die Betriebsregeln des Thread-Pools klarstellt, wodurch einige unbekannte Risiken vermieden werden können.

Acho que você gosta

Origin blog.csdn.net/sco5282/article/details/120963463
Recomendado
Clasificación