Heim  >  Artikel  >  Java  >  Analyse von vier Java-Thread-Pool-Nutzungen

Analyse von vier Java-Thread-Pool-Nutzungen

高洛峰
高洛峰Original
2017-01-23 16:26:041449Durchsuche

In diesem Artikel werden vier Java-Thread-Pool-Nutzungen als Referenz analysiert.

Nachteile des neuen Threads

Um eine asynchrone Aufgabe auszuführen, verwenden Sie einfach Neuer Thread wie folgt?

new Thread(new Runnable() {
 
  @Override
  public void run() {
    // TODO Auto-generated method stub
    }
  }
).start();

Dann haben Sie zu viele Outs. Die Nachteile eines neuen Threads sind wie folgt:

a. Jedes Mal, wenn ein neuer Thread ein Objekt erstellt arm.
b. Threads fehlt eine einheitliche Verwaltung, es kann unbegrenzt viele neue Threads geben, sie konkurrieren miteinander und belegen möglicherweise zu viele Systemressourcen, was zu Abstürzen oder OOM führt.
c. Fehlen weiterer Funktionen, wie z. B. geplante Ausführung, periodische Ausführung und Thread-Unterbrechung.

Im Vergleich zu neuen Threads bieten die vier von Java bereitgestellten Thread-Pools folgende Vorteile:

a. Vorhandene Threads wiederverwenden, die Kosten für die Objekterstellung und -zerstörung reduzieren und eine gute Leistung erzielen.
b. Es kann die maximale Anzahl gleichzeitiger Threads effektiv steuern, die Nutzung von Systemressourcen verbessern und übermäßigen Ressourcenwettbewerb und Überlastung vermeiden.
c. Bietet Funktionen wie geplante Ausführung, periodische Ausführung, Einzelthread und Parallelitätskontrolle.

2. Java-Thread-Pool

Java stellt über Executors vier Thread-Pools bereit, nämlich:

newCachedThreadPool erstellt einen zwischenspeicherbaren Thread-Pool, der den Verarbeitungsbedarf überschreitet kann inaktive Threads flexibel recyceln. Wenn keine Möglichkeit zum Recyceln besteht, wird ein neuer Thread erstellt.
newFixedThreadPool erstellt einen Thread-Pool mit fester Länge, der die maximale Anzahl gleichzeitiger Threads steuern kann, die in der Warteschlange warten.
newScheduledThreadPool erstellt einen Thread-Pool fester Länge, der die geplante und periodische Aufgabenausführung unterstützt.
newSingleThreadExecutor erstellt einen Single-Threaded-Thread-Pool, der nur einen eindeutigen Arbeitsthread zum Ausführen von Aufgaben verwendet und sicherstellt, dass alle Aufgaben in der angegebenen Reihenfolge (FIFO, LIFO, Priorität) ausgeführt werden.
(1)newCachedThreadPool:

Erstellen Sie einen zwischenspeicherbaren Thread-Pool. Wenn die Länge des Thread-Pools den Verarbeitungsbedarf überschreitet, können inaktive Threads flexibel recycelt werden . Der Beispielcode lautet wie folgt:

ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
  for (int i = 0; i < 10; i++) {
    final int index = i;
  try {
    Thread.sleep(index * 1000);
  }
    catch (InterruptedException e) {
      e.printStackTrace();
  }
 
cachedThreadPool.execute(new Runnable() {
 
@Override
public void run() {
  System.out.println(index);
}
});
}


Der Thread-Pool ist unendlich. Wenn die zweite Aufgabe ausgeführt wird, ist die erste Aufgabe abgeschlossen und wird wiederholt. Verwenden Sie den Thread, der die erste Aufgabe ausführt, anstatt jedes Mal einen neuen Thread zu erstellen.

(2)newFixedThreadPool:

Erstellen Sie einen Thread-Pool fester Länge, der die maximale Anzahl gleichzeitiger Threads steuern kann, und überschüssige Threads warten in der Warteschlange. Der Beispielcode lautet wie folgt:

ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
  for (int i = 0; i < 10; i++) {
  final int index = i;
 
  fixedThreadPool.execute(new Runnable() {
 
@Override
public void run() {
try {
  System.out.println(index);
  Thread.sleep(2000);
} catch (InterruptedException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
  }
}
});
}

Da die Thread-Pool-Größe 3 beträgt, schläft jede Aufgabe nach der Ausgabe des Index 2 Sekunden lang, also 3 Zahlen werden alle zwei Sekunden gedruckt.

Die Größe des Thread-Pools fester Länge wird am besten entsprechend den Systemressourcen festgelegt. Wie zum Beispiel Runtime.getRuntime().availableProcessors(). Weitere Informationen finden Sie unter PreloadDataCache.

(3)newScheduledThreadPool:

Erstellen Sie einen Thread-Pool fester Länge, um die geplante und periodische Aufgabenausführung zu unterstützen. Der Beispielcode für die verzögerte Ausführung lautet wie folgt:

ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
 scheduledThreadPool.schedule(new Runnable() {
 
@Override
public void run() {
  System.out.println("delay 3 seconds");
}
}, 3, TimeUnit.SECONDS);

bedeutet verzögerte Ausführung für 3 Sekunden.

Der Beispielcode für die reguläre Ausführung lautet wie folgt:

scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
 
@Override
public void run() {
  System.out.println("delay 1 seconds, and excute every 3 seconds");
}
}, 1, 3, TimeUnit.SECONDS);

bedeutet die Ausführung alle 3 Sekunden nach einer Verzögerung von 1 Sekunde.

ScheduledExecutorService ist sicherer und leistungsfähiger als Timer

(4) newSingleThreadExecutor:

Erstellen Sie einen Single-Threaded-Thread-Pool, der nur den einzigen Arbeitsthread verwendet um sicherzustellen, dass alle Aufgaben in der angegebenen Reihenfolge (FIFO, LIFO, Priorität) ausgeführt werden. Der Beispielcode lautet wie folgt:

ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int index = i;
singleThreadExecutor.execute(new Runnable() {
 
@Override
public void run() {
  try {
    System.out.println(index);
  Thread.sleep(2000);
} catch (InterruptedException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
    }
}
  });
}

Die Ergebnisse werden nacheinander ausgegeben, was der Ausführung jeder Aufgabe nacheinander entspricht.

Die meisten aktuellen GUI-Programme sind Single-Threaded. Einzelne Threads in Android können für Datenbankvorgänge, Dateivorgänge, Stapelinstallationen von Anwendungen, Stapellöschungen von Anwendungen und andere Vorgänge verwendet werden, die nicht für die Parallelität geeignet sind, aber zu E/A-Blockierungen führen und die Reaktion des UI-Threads beeinträchtigen können.

Die Rolle des Thread-Pools:

Die Rolle des Thread-Pools besteht darin, die Anzahl der Ausführungsthreads im System zu begrenzen.
Je nach Systemumgebung kann die Anzahl der Threads automatisch oder manuell eingestellt werden, um den besten Betriebseffekt zu erzielen. Weniger verschwendet Systemressourcen und mehr führt zu Systemüberlastung und Ineffizienz. Verwenden Sie den Thread-Pool, um die Anzahl der Threads zu steuern, und andere Threads warten in der Schlange. Nachdem eine Aufgabe ausgeführt wurde, wird die vorderste Aufgabe in der Warteschlange übernommen und die Ausführung beginnt. Wenn sich in der Warteschlange kein Warteprozess befindet, wartet diese Ressource des Thread-Pools. Wenn eine neue Aufgabe ausgeführt werden muss und sich im Thread-Pool wartende Arbeitsthreads befinden, kann sie ausgeführt werden. Andernfalls gelangt sie in die Warteschlange.

Warum Thread-Pool verwenden:

1. Reduzieren Sie die Anzahl der Thread-Erstellung und -Zerstörung, jeder Arbeitsthread kann wiederverwendet werden und mehrere Aufgaben ausführen.

2. Sie können die Anzahl der Arbeitsthreads im Thread-Pool entsprechend der Kapazität des Systems anpassen, um zu verhindern, dass der Server aufgrund übermäßigen Speicherverbrauchs erschöpft wird (jeder Thread benötigt etwa 1 MB Speicher und der Thread startet). Je mehr Speicher verbraucht wird, desto mehr Speicher wird verbraucht und schließlich stürzt das System ab.

Die Schnittstelle der obersten Ebene des Thread-Pools in Java ist Executor, aber streng genommen ist Executor kein Thread-Pool, sondern lediglich ein Tool zum Ausführen von Threads. Die eigentliche Thread-Pool-Schnittstelle ist ExecutorService.

Einige der wichtigeren Klassen:

ExecutorService: Die echte Thread-Pool-Schnittstelle.

ScheduledExecutorService: Ähnlich wie Timer/TimerTask kann es Probleme lösen, die eine wiederholte Ausführung von Aufgaben erfordern.

ThreadPoolExecutor: Die Standardimplementierung von ExecutorService.

ScheduledThreadPoolExecutor: Erbt die ScheduledExecutorService-Schnittstellenimplementierung von ThreadPoolExecutor, einer Klassenimplementierung der periodischen Aufgabenplanung.

要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优的,因此在Executors类里面提供了一些静态工厂,生成一些常用的线程池。

1.newSingleThreadExecutor

创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

2.newFixedThreadPool

创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。

3.newCachedThreadPool

创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,

那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。

4.newScheduledThreadPool

创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。

实例代码

一、固定大小的线程池,newFixedThreadPool:

package app.executors;
 
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
 
/**
 * Java线程:线程池
 *
 * @author xiho
 */
public class Test {
  public static void main(String[] args) {
    // 创建一个可重用固定线程数的线程池
    ExecutorService pool = Executors.newFixedThreadPool(2);
    // 创建线程
    Thread t1 = new MyThread();
    Thread t2 = new MyThread();
    Thread t3 = new MyThread();
    Thread t4 = new MyThread();
    Thread t5 = new MyThread();
    // 将线程放入池中进行执行
    pool.execute(t1);
    pool.execute(t2);
    pool.execute(t3);
    pool.execute(t4);
    pool.execute(t5);
    // 关闭线程池
    pool.shutdown();
  }
}
 
class MyThread extends Thread {
  @Override
  public void run() {
    System.out.println(Thread.currentThread().getName() + "正在执行。。。");
  }
}

输出结果:

pool-1-thread-1正在执行。。。
pool-1-thread-3正在执行。。。
pool-1-thread-4正在执行。。。
pool-1-thread-2正在执行。。。
pool-1-thread-5正在执行。。。

改变ExecutorService pool = Executors.newFixedThreadPool(5)中的参数:ExecutorService pool = Executors.newFixedThreadPool(2),输出结果是:

pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-2正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-2正在执行。。。

从以上结果可以看出,newFixedThreadPool的参数指定了可以运行的线程的最大数目,超过这个数目的线程加进去以后,不会运行。其次,加入线程池的线程属于托管状态,线程的运行不受加入顺序的影响。

二、单任务线程池,newSingleThreadExecutor:

仅仅是把上述代码中的ExecutorService pool = Executors.newFixedThreadPool(2)改为ExecutorService pool = Executors.newSingleThreadExecutor();
输出结果:

pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。

可以看出,每次调用execute方法,其实最后都是调用了thread-1的run方法。

三、可变尺寸的线程池,newCachedThreadPool:

与上面的类似,只是改动下pool的创建方式:ExecutorService pool = Executors.newCachedThreadPool();

输出结果:

pool-1-thread-1正在执行。。。
pool-1-thread-2正在执行。。。
pool-1-thread-4正在执行。。。
pool-1-thread-3正在执行。。。
pool-1-thread-5正在执行。。。

这种方式的特点是:可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。

四、延迟连接池,newScheduledThreadPool:

public class TestScheduledThreadPoolExecutor {
 
  public static void main(String[] args) {
 
    ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(1);
 
    exec.scheduleAtFixedRate(new Runnable() {//每隔一段时间就触发异常
 
           @Override
 
           publicvoid run() {
 
              //throw new RuntimeException();
 
              System.out.println("================");
 
           }
 
         }, 1000, 5000, TimeUnit.MILLISECONDS);
 
    exec.scheduleAtFixedRate(new Runnable() {//每隔一段时间打印系统时间,证明两者是互不影响的
 
           @Override
 
           publicvoid run() {
 
              System.out.println(System.nanoTime());
 
           }
 
         }, 1000, 2000, TimeUnit.MILLISECONDS);
 
  }
 
}

   

输出结果:

================
 
8384644549516
 
8386643829034
 
8388643830710
 
================
 
8390643851383
 
8392643879319
 
8400643939383

   

以上就是本文的全部内容,希望对大家的学习有所帮助。

更多四种Java线程池用法解析相关文章请关注PHP中文网!

Stellungnahme:
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