Heim  >  Artikel  >  Java  >  So erstellen Sie einen Java-Thread-Pool

So erstellen Sie einen Java-Thread-Pool

PHPz
PHPznach vorne
2023-05-16 08:43:123249Durchsuche

Die Vorteile des Thread-Pools

  • können die Wiederverwendung von Threads realisieren und das erneute Erstellen und Zerstören von Threads vermeiden. Das Erstellen und Zerstören von Threads ist für die CPU sehr kostspielig.

  • Sie können die maximale Anzahl der Threads begrenzen, die erstellt werden können, und die Thread-Pool-Parameter dynamisch an die Leistung Ihres eigenen Computers anpassen, um die Anwendungsleistung zu verbessern.

  • Bietet Funktionen wie geplante Ausführung und Parallelitätskontrolle.

  • Einheitliche Verwaltung von Threads.

Fünf Möglichkeiten, einen Thread-Pool zu erstellen

1: Cache-Thread-Pool (nicht empfohlen)

2: Thread-Pool mit fester Kapazität (nicht empfohlen)

3: Einzelner Thread-Pool (nicht empfohlen)

4: Timing Aufgaben-Thread-Pool (nicht empfohlen)

5: Erstellen Sie einen Thread-Pool über die ThreadPoolExecutor-Konstruktionsmethode (im Alibaba-Entwicklungshandbuch dringend empfohlen)

Die ersten vier Möglichkeiten zum Erstellen eines Thread-Pools werden alle über die statischen Methoden von Executors erstellt.

CachedThreadPool

	ExecutorService executorService = Executors.newCachedThreadPool();

        for (int i = 0; i < 10; i++) {
            final int finalI = i;
            executorService.execute(new Runnable() {
                public void run() {
                    System.out.println(Thread.currentThread().getName()+"<thread->run>"+ finalI);
                }
            });
        }

Warum wird der zwischengespeicherte Thread-Pool nicht empfohlen?

Quellcode-Analyse

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, 2147483647, 60L, TimeUnit.SECONDS, new SynchronousQueue());
    }
 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler);
    }

Anhand der beiden oben genannten Codeausschnitte können wir sehen, dass die maximale PoolSize von CachedThreadPool der Maximalwert von Integer 2147483647 ist, was einer unbegrenzten Thread-Erstellung entspricht, und das Erstellen von Threads erfordert Speicher, was zu einem Speicherüberlauf führt , und normale Maschinen benötigen keinen so großen Speicher, um eine so große Anzahl von Threads zu erstellen.

Thread-Pool mit fester Kapazität FixedThreadPool

newFixedThreadPool(int num), num ist die feste Anzahl von Threads, die wir angeben möchten

	ExecutorService executorService = Executors.newFixedThreadPool(5);

      for (int i = 0; i < 10; i++) {
          final int finalI = i;
          executorService.execute(new Runnable() {
              public void run() {
                  System.out.println(Thread.currentThread().getName()+"<thread->run>"+ finalI);
              }
          });
      }

Ausgabe:

pool-1-thread-575b3c38ff368dcafe01d2cfbd3d86ea2run>4
pool- 1-thread-475b3c38ff368dcafe01d2cfbd3d86ea2run>3
pool-1-thread-575b3c38ff368dcafe01d2cfbd3d86ea2run>5
pool-1-thread-375b3c38ff368dcafe01d2cfbd3d86ea2run>2
pool- 1- thread-375b3c38ff368dcafe01d2cfbd3d86ea2run>8
pool-1-thread-375b3c38ff368dcafe01d2cfbd3d86ea2run>9
pool-1-thread-275b3c38ff368dcafe01d2cfbd3d86ea2run>1
pool-1- thread- 175b3c38ff368dcafe01d2cfbd3d86ea2run>0
pool-1-thread-575b3c38ff368dcafe01d2cfbd3d86ea2run>7
pool-1-thread-475b3c38ff368dcafe01d2cfbd3d86ea2run>6

Es ist zu sehen dass Threads zur Wiederverwendung verwendet werden.

Warum ist FixedThreadPool ein fester Thread-Pool?

Quellcode-Analyse

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue());
    }

Aus diesem Quellcode ist ersichtlich, dass die Anzahl der Kern-Threads (corePoolSize) und die maximale Anzahl von Threads (maximumPoolSize) beide nThreads sind, da der Thread-Pool nur auf diese Weise nicht vorhanden ist erweitert und die Anzahl der Threads wird festgelegt.

Single-Thread-Pool SingleThreadExecutor

	ExecutorService executorService = Executors.newSingleThreadExecutor();

      for (int i = 0; i < 10; i++) {
          final int finalI = i;
          executorService.execute(new Runnable() {
              public void run() {
                  System.out.println(Thread.currentThread().getName()+"<thread->run>"+ finalI);
              }
          });

      }

Warum enthält SingleThreadExecutor nur einen Thread?

Quellcode-Analyse

public static ExecutorService newSingleThreadExecutor() {
        return new Executors.FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()));
    }

Aus diesem Quellcode ist ersichtlich, dass die Anzahl der Kernthreads (corePoolSize) und die maximale Anzahl von Threads (maximumPoolSize) jeweils 1 sind, sodass nur ein Thread enthalten ist.

ScheduledThreadPool

	  int initDelay=10; //初始化延时
      int period=1;//初始化延迟过了之后,每秒的延时

      ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);

      scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
          @Override
          public void run() {
              System.out.println(Thread.currentThread().getName()+"<thread->run>");
          }
      },initDelay,period, TimeUnit.SECONDS);

Die Wirkung dieses Codes ist: Warten Sie 10 Sekunden, nachdem das Programm ausgeführt wurde, geben Sie dann das erste Ergebnis aus und geben Sie das Ergebnis dann alle 1 Sekunde aus.

Warum wird ScheduledThreadPool nicht empfohlen?

Quellcode-Analyse

public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, 2147483647, 10L, TimeUnit.MILLISECONDS, new ScheduledThreadPoolExecutor.DelayedWorkQueue());
    }

Es ist ersichtlich, dass die maximale Anzahl von Threads (maximumPoolSize) von ScheduledThreadPool dem Maximalwert von Integer 2147483647 entspricht, was einer unbegrenzten Thread-Erstellung entspricht, und das Erstellen von Threads erfordert Speicher, was zu einem Speicherüberlauf führt , und im Allgemeinen Die Maschine verwendet keinen so großen Speicher, um eine so große Anzahl von Threads zu erstellen.

ThreadPoolExecutor erstellt einen Thread-Pool (sehr zu empfehlen)

	ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 20,
              2L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5),
              Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());

      for (int i = 0; i < 12; i++) {
          final int finalI = i;
          threadPoolExecutor.execute(new Runnable() {
              public void run() {
                  System.out.println(Thread.currentThread().getName()+"<thread->run>"+ finalI);
              }
          });
      }
Detaillierte Erklärung der sieben Parameter von ThreadPoolExecutor
	public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
        
    }
  • corePoolSize: die Anzahl der Kern-Threads. Sobald diese Threads erstellt wurden, werden sie nicht zerstört und bleiben immer bestehen. Der Thread-Pool verfügt standardmäßig über keine Threads. Wenn eine Aufgabe eintrifft, wird über ThreadFactory ein Thread erstellt, der immer vorhanden ist.

  • maximumPoolSize: Maximale Anzahl von Threads. Die Anzahl der Nicht-Kern-Threads = MaximumPoolSize-CorePoolSize. Die Anzahl der Nicht-Kern-Threads entspricht tatsächlich der Anzahl der skalierbaren Threads und kann zerstört werden.

  • keepAliveTime: Die Leerlaufzeit von Nicht-Kern-Threads. Wenn die Anzahl der durch die Erweiterung generierten Nicht-Kern-Threads nach der keepAliveTime immer noch im Leerlauf ist, werden diese Nicht-Kern-Threads zerstört.

  • Einheit: Zeiteinheit von keepAliveTime, zum Beispiel: Sekunden

  • workQueue: Wartebereich. Wenn eine Aufgabe > corePoolSize eintrifft, wird die Aufgabe in der blockierenden Warteschlange von workQueue gespeichert und wartet darauf, dass andere Threads sie verarbeiten.

  • threadFactory: Thread-Fabrik. Eine Möglichkeit, Threads zu erstellen.

  • Handler: Ablehnungsstrategie. Wenn es um > die maximale Anzahl von Threads + die Kapazität von workQueue geht, wird die Ablehnungsrichtlinie ausgeführt

workQueue

ArrayBlockingQueue: begrenzte Blockierungswarteschlange. Die Warteschlange hat eine Größenbeschränkung. Wenn die Kapazität überschritten wird, wird die Erweiterungs- oder Ablehnungsrichtlinie ausgelöst.

	public ArrayBlockingQueue(int capacity) {
        this(capacity, false);
    }

LinkedBlockingQueue: Unbegrenzte Blockierungswarteschlange, die Warteschlange hat keine Größenbeschränkung und kann zu einem Speicherüberlauf führen.

	 public LinkedBlockingQueue() {
        this(2147483647);
    }
handler

AbortPolicy: Eine Ausnahme direkt auslösen

	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());
        }
    }

DiscardPolicy: Es wird keine Operation ausgeführt. Verwerfen Sie die Aufgabe stillschweigend.

	public static class DiscardPolicy implements RejectedExecutionHandler {
        public DiscardPolicy() {
        }

        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        }
    }

DiscardOldestPolicy: Verwerfen Sie die am längsten vorhandene Aufgabe

	ThreadFactory threadFactory = Executors.defaultThreadFactory();

      threadFactory.newThread(new Runnable() {
          @Override
          public void run() {
              System.out.println("threadFactory");
          }
      }).start();

如何触发拒绝策略和线程池扩容?
	ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 20,
              2L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5),
              Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());

      for (int i = 0; i < 26; i++) { //并发数26
          final int finalI = i;
          threadPoolExecutor.execute(new Runnable() {
              public void run() {
                  System.out.println(Thread.currentThread().getName()+"<thread->run>"+ finalI);
              }
          });
      }
      /**
       * 核心线程数=10,最大线程数=20,故可扩容线程数=20-10
       * BlockingQueue的大小为5,故等待区的大小为5,也就是当并发数<=核心线程数+5不会扩容,并发数大于16才会扩容
       *
       * 触发扩容:并发数>核心线程数+阻塞队列的大小
       * 对于这段代码,如果来了26个并发,10个并发会被核心线程处理,5个会在等待区,剩下11个会因为等待区满了而触发扩容
       * 因为这里最多能够扩容10个,这里却是11个,所以会触发拒绝策略
       */
  • 为什么这段代码会触发拒绝策略

对于这段代码,如果来了26个并发,10个并发会被核心线程处理,5个会在等待区,剩下11个会因为等待区满了而触发扩容,但是又因为因为这里最多能够扩容10个,这里却是11个,所以会触发拒绝策略。

  • 怎么触发扩容

触发扩容:并发数>核心线程数(corePoolSize)+阻塞队列(workQueue)的大小

  • 使用Java纯手写一个线程池

Das obige ist der detaillierte Inhalt vonSo erstellen Sie einen Java-Thread-Pool. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:yisu.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen