Chihua-Designidee
Chihua-Design sollte kein neuer Begriff sein. Unsere gängigen Methoden wie Java-Thread-Pool, JDBC-Verbindungspool, Redis-Verbindungspool usw. sind repräsentative Implementierungen dieser Art von Design.
Bei diesem Entwurf werden zunächst Ressourcen voreingestellt. Das gelöste Problem besteht darin, den Verbrauch jedes Ressourcenerwerbs auszugleichen, z. B. die Kosten für die Erstellung von Threads, die Kosten für den Erhalt von Remoteverbindungen usw. Genau wie wenn Sie in die Kantine gehen, um Essen zu holen, stellt die Tante, die das Essen zubereitet, zuerst mehrere Portionen Reis dorthin. Wenn Sie kommen, können Sie einfach die Lunchbox nehmen und Gemüse hinzufügen Reis und Gemüse gleichzeitig zubereiten, was effizienter ist.
Neben der Initialisierung von Ressourcen umfasst das Pooling-Design auch die folgenden Funktionen: den Anfangswert des Pools, den aktiven Wert des Pools, den Maximalwert des Pools usw. Diese Funktionen können direkt sein in den Mitgliedsattributen dem Java-Thread-Pool und dem Datenbankverbindungspool zugeordnet.
Der Zeitpunkt, zu dem der Thread-Pool die Ablehnungsrichtlinie auslöst
unterscheidet sich zusätzlich zur anfänglichen Größe und Der maximale Poolwert und der Thread-Pool verfügen über eine zusätzliche Blockierungswarteschlange zum Puffern.
Der Datenquellen-Verbindungspool löst im Allgemeinen eine Ablehnungsrichtlinie aus, wenn die Anzahl der angeforderten Verbindungen den Maximalwert des Verbindungspools überschreitet. Die Richtlinie besteht im Allgemeinen darin, zu blockieren und auf die festgelegte Zeit zu warten oder direkt eine Ausnahme auszulösen .
Wie in der Abbildung gezeigt, müssen Sie die spezifische Bedeutung der oben genannten drei klären, wenn Sie wissen möchten, wann der Thread-Pool eine Ablehnung auslöst Parameter, das ist die Gesamtbedeutung dieser drei Parameter, anstatt einfach die maximale Anzahl von Threads zu überschreiten, wird eine Thread-Ablehnung ausgelöst. Wenn die Anzahl der übermittelten Aufgaben größer als corePoolSize ist, werden sie in die Warteschlange gestellt Erst nachdem der Puffer gefüllt ist, wird beurteilt, ob die Aufgabe größer als maxPoolSize ist. Wenn sie kleiner als maxPoolSize ist, wird ein neuer Thread zur Verarbeitung erstellt. Wenn er größer als der Wert ist, wird die Ablehnungsrichtlinie ausgelöst.
Die Zusammenfassung lautet: Wenn die aktuelle Anzahl der übermittelten Aufgaben größer als (maxPoolSize + queueCapacity) ist, wird die Ablehnungsrichtlinie des Thread-Pools ausgelöst.
JDK verfügt über 4 integrierte Thread-Pool-Ablehnungsstrategien. #🎜 🎜#Bevor Sie die mit JDK gelieferte Thread-Pool-Ablehnungsrichtlinie analysieren, sehen Sie sich zunächst die von JDK definierte Ablehnungsrichtlinienschnittstelle wie folgt an:
public interface RejectedExecutionHandler { void rejectedExecution(Runnable r, ThreadPoolExecutor executor); }Die Schnittstellendefinition ist bei der Ablehnungsrichtlinie sehr klar Wird ausgelöst, ruft der Thread-Pool auf. Die von Ihnen festgelegte spezifische Strategie übergibt die aktuell übermittelte Aufgabe und die Thread-Pool-Instanz selbst zur Verarbeitung. Für verschiedene Szenarien gelten unterschiedliche Überlegungen. Sehen wir uns an, welche Implementierungen das JDK für uns integriert hat. 🎜🎜## 🎜🎜#
CallerRunsPolicy (Caller Run Policy)
public static class CallerRunsPolicy implements RejectedExecutionHandler { public CallerRunsPolicy() { } public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (!e.isShutdown()) { r.run(); } } }
Funktion:
Wenn die Ablehnungsrichtlinie ausgelöst wird, solange Der Thread-Pool ist nicht geschlossen. Wird vom aktuellen Thread verarbeitet, der die Aufgabe übermittelt hat.Verwendungsszenarien: Wird im Allgemeinen in Szenarien verwendet, in denen Fehler nicht zulässig sind, die Leistungsanforderungen nicht hoch sind und die Parallelität gering ist, da der Thread-Pool im Normalfall nicht geschlossen wird Das heißt, die übermittelte Aufgabe wird definitiv ausgeführt. Da sie jedoch vom Aufrufer-Thread selbst ausgeführt wird, blockiert sie bei mehrfacher Übermittlung der Aufgabe die Ausführung nachfolgender Aufgaben und beeinträchtigt natürlich die Leistung und Effizienz langsam.
AbortPolicy (Abbruchrichtlinie)
public static class AbortPolicy implements RejectedExecutionHandler { public AbortPolicy() { } public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { throw new RejectedExecutionException("Task " + r.toString() + " rejected from " + e.toString()); } }
Funktion: Wenn die Ablehnungsrichtlinie ausgelöst wird, werfen Sie die Ablehnung Direkte Ausführungsausnahmen, die Abbruchstrategie bedeutet, den aktuellen Ausführungsprozess zu unterbrechen.
Die Standardrichtlinie in ThreadPoolExecutor ist AbortPolicy. Die Serie ThreadPoolExecutor der ExecutorService-Schnittstelle zeigt die Ablehnungsrichtlinie nicht an, daher ist die Standardrichtlinie diese.Bitte beachten Sie jedoch, dass die Thread-Pool-Instanzwarteschlange in ExecutorService unbegrenzt ist, was bedeutet, dass die Ablehnungsrichtlinie nicht ausgelöst wird, selbst wenn der Speicher erschöpft ist. Wenn Sie eine Thread-Pool-Instanz anpassen, müssen Sie bei Verwendung dieser Strategie die Ausnahme behandeln, die beim Auslösen der Strategie ausgelöst wird, da dadurch der aktuelle Ausführungsprozess unterbrochen wird.
DiscardPolicy
public static class DiscardPolicy implements RejectedExecutionHandler { public DiscardPolicy() { } public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { } }
Funktion: Diese Aufgabe leise verwerfen, ohne eine Aktion auszulösen #🎜 🎜#
Verwendungsszenario:Sie können es verwenden, wenn die von Ihnen eingereichte Aufgabe nicht wichtig ist. Weil es eine leere Implementierung ist und Ihre Aufgaben stillschweigend verschlingt. Diese Strategie wird also grundsätzlich nicht mehr benötigt Thread Wenn der Pool nicht geschlossen ist, öffnen Sie das Element am Kopf der Warteschlange und versuchen Sie dann, es auszuführen
Verwendungsszenario:Diese Strategie verwirft die Aufgabe trotzdem , und es wird beim Verwerfen stumm sein, aber es hat die Eigenschaften Was verworfen wird, sind alte, nicht ausgeführte Aufgaben und Aufgaben mit einer höheren Priorität, die ausgeführt werden müssen. Basierend auf dieser Funktion kann ich mir vorstellen, eine Nachricht zu veröffentlichen und eine Nachricht zu ändern. Zu diesem Zeitpunkt wurde die Nachricht noch nicht ausgeführt , und die Nachricht wird zu diesem Zeitpunkt nicht ausgeführt. Die Nachrichtenversion ist niedriger als die aktuell übermittelte Nachrichtenversion und kann verworfen werden. Da sich möglicherweise Nachrichten mit niedrigeren Nachrichtenversionen in der Warteschlange befinden, die zur Ausführung in die Warteschlange gestellt werden, müssen Sie die Nachrichtenversionen bei der tatsächlichen Verarbeitung der Nachricht vergleichen.
Von Drittanbietern implementierte Ablehnungsstrategie
dubbo中的线程拒绝策略
public class AbortPolicyWithReport extends ThreadPoolExecutor.AbortPolicy { protected static final Logger logger = LoggerFactory.getLogger(AbortPolicyWithReport.class); private final String threadName; private final URL url; private static volatile long lastPrintTime = 0; private static Semaphore guard = new Semaphore(1); public AbortPolicyWithReport(String threadName, URL url) { this.threadName = threadName; this.url = url; } @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { String msg = String.format("Thread pool is EXHAUSTED!" + " Thread Name: %s, Pool Size: %d (active: %d, core: %d, max: %d, largest: %d), Task: %d (completed: %d)," + " Executor status:(isShutdown:%s, isTerminated:%s, isTerminating:%s), in %s://%s:%d!", threadName, e.getPoolSize(), e.getActiveCount(), e.getCorePoolSize(), e.getMaximumPoolSize(), e.getLargestPoolSize(), e.getTaskCount(), e.getCompletedTaskCount(), e.isShutdown(), e.isTerminated(), e.isTerminating(), url.getProtocol(), url.getIp(), url.getPort()); logger.warn(msg); dumpJStack(); throw new RejectedExecutionException(msg); } private void dumpJStack() { //省略实现 } }
可以看到,当dubbo的工作线程触发了线程拒绝后,主要做了三个事情,原则就是尽量让使用者清楚触发线程拒绝策略的真实原因。
1)输出了一条警告级别的日志,日志内容为线程池的详细设置参数,以及线程池当前的状态,还有当前拒绝任务的一些详细信息。可以说,这条日志,使用dubbo的有过生产运维经验的或多或少是见过的,这个日志简直就是日志打印的典范,其他的日志打印的典范还有spring。得益于这么详细的日志,可以很容易定位到问题所在
2)输出当前线程堆栈详情,这个太有用了,当你通过上面的日志信息还不能定位问题时,案发现场的dump线程上下文信息就是你发现问题的救命稻草。
3)继续抛出拒绝执行异常,使本次任务失败,这个继承了JDK默认拒绝策略的特性
Netty中的线程池拒绝策略
private static final class NewThreadRunsPolicy implements RejectedExecutionHandler { NewThreadRunsPolicy() { super(); } public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { try { final Thread t = new Thread(r, "Temporary task executor"); t.start(); } catch (Throwable e) { throw new RejectedExecutionException( "Failed to start a new thread", e); } } }
Netty中的实现很像JDK中的CallerRunsPolicy,舍不得丢弃任务。不同的是,CallerRunsPolicy是直接在调用者线程执行的任务。而 Netty是新建了一个线程来处理的。
所以,Netty的实现相较于调用者执行策略的使用面就可以扩展到支持高效率高性能的场景了。但是也要注意一点,Netty的实现里,在创建线程时未做任何的判断约束,也就是说只要系统还有资源就会创建新的线程来处理,直到new不出新的线程了,才会抛创建线程失败的异常。
activeMq中的线程池拒绝策略
new RejectedExecutionHandler() { @Override public void rejectedExecution(final Runnable r, final ThreadPoolExecutor executor) { try { executor.getQueue().offer(r, 60, TimeUnit.SECONDS); } catch (InterruptedException e) { throw new RejectedExecutionException("Interrupted waiting for BrokerService.worker"); } throw new RejectedExecutionException("Timed Out while attempting to enqueue Task."); } });
activeMq中的策略属于最大努力执行任务型,当触发拒绝策略时,在尝试一分钟的时间重新将任务塞进任务队列,当一分钟超时还没成功时,就抛出异常
pinpoint中的线程池拒绝策略
public class RejectedExecutionHandlerChain implements RejectedExecutionHandler { private final RejectedExecutionHandler[] handlerChain; public static RejectedExecutionHandler build(List<rejectedexecutionhandler> chain) { Objects.requireNonNull(chain, "handlerChain must not be null"); RejectedExecutionHandler[] handlerChain = chain.toArray(new RejectedExecutionHandler[0]); return new RejectedExecutionHandlerChain(handlerChain); } private RejectedExecutionHandlerChain(RejectedExecutionHandler[] handlerChain) { this.handlerChain = Objects.requireNonNull(handlerChain, "handlerChain must not be null"); } @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { for (RejectedExecutionHandler rejectedExecutionHandler : handlerChain) { rejectedExecutionHandler.rejectedExecution(r, executor); } } }</rejectedexecutionhandler>
pinpoint的拒绝策略实现很有特点,和其他的实现都不同。他定义了一个拒绝策略链,包装了一个拒绝策略列表,当触发拒绝策略时,会将策略链中的rejectedExecution依次执行一遍。
Das obige ist der detaillierte Inhalt vonWas ist die Ablehnungsrichtlinie des Java-Thread-Pools?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!