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

So erstellen Sie einen Java-Thread-Pool

PHPz
Freigeben: 2023-05-16 08:43:12
nach vorne
3354 Leute haben es durchsucht

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);
                }
            });
        }
Nach dem Login kopieren

Warum wird der zwischengespeicherte Thread-Pool nicht empfohlen?

Quellcode-Analyse

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

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);
              }
          });
      }
Nach dem Login kopieren

Ausgabe:

pool-1-thread-5run>4
pool- 1-thread-4run>3
pool-1-thread-5run>5
pool-1-thread-3run>2
pool- 1- thread-3run>8
pool-1-thread-3run>9
pool-1-thread-2run>1
pool-1- thread- 1run>0
pool-1-thread-5run>7
pool-1-thread-4run>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());
    }
Nach dem Login kopieren

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

      }
Nach dem Login kopieren

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()));
    }
Nach dem Login kopieren

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);
Nach dem Login kopieren

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());
    }
Nach dem Login kopieren

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);
              }
          });
      }
Nach dem Login kopieren
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) {
        
    }
Nach dem Login kopieren
  • 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);
    }
Nach dem Login kopieren

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

	 public LinkedBlockingQueue() {
        this(2147483647);
    }
Nach dem Login kopieren
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());
        }
    }
Nach dem Login kopieren

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) {
        }
    }
Nach dem Login kopieren

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();
Nach dem Login kopieren
如何触发拒绝策略和线程池扩容?
	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个,所以会触发拒绝策略
       */
Nach dem Login kopieren
  • 为什么这段代码会触发拒绝策略

对于这段代码,如果来了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!

Verwandte Etiketten:
Quelle:yisu.com
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage