Mehrere M?glichkeiten, Multithreading in Java zu implementieren
Jan 04, 2023 pm 03:52 PMSo implementieren Sie Multithreading: 1. Erben Sie die Thread-Klasse und schreiben Sie die Ausführungsmethode der Thread-Klasse über die von JDK bereitgestellte Thread-Klasse neu. 2. Implementieren Sie die Runnable-Schnittstelle. Runnable ist eine funktionale Schnittstelle ?@FunctionalInterface“. Dies bedeutet, dass Sie die von JDK8 bereitgestellte Lambda-Methode verwenden k?nnen. 3. Verwenden Sie interne Klassen. 5. Thread-Implementierung mit Rückgabewerten.
Die Betriebsumgebung dieses Tutorials: Windows7-System, Java8-Version, DELL G3-Computer.
Es gibt zwei Hauptmethoden, um Multithreading in Formularen zu implementieren: Eine besteht darin, die Thread-Klasse zu erben, und die andere darin, die Runnable-Schnittstelle zu implementieren. Im Wesentlichen besteht die Implementierungsmethode darin, die Thread-Aufgabe zu implementieren und dann den Thread zu starten, um die Thread-Aufgabe auszuführen (die Thread-Aufgabe ist hier eigentlich die Ausführungsmethode). Bei den hier genannten 6 Typen handelt es sich tats?chlich um Modifikationen, die auf den beiden oben genannten Typen basieren.
Das Folgende ist eine Einführung in diese 6 Implementierungsmethoden nacheinander.
Der erste Weg: Erben Sie die Thread-Klasse
Alles ist ein Objekt, dann sind Threads auch Objekte, und Objekte sollten in der Lage sein, ihre ?ffentlichen Eigenschaften zu extrahieren und sie in Klassen zu kapseln, um mehrere zu instanziieren Objekte und dann implementieren. Die erste M?glichkeit, Threads zu verwenden, besteht darin, ?die Thread-Klasse zu erben“. Das Erben der Thread-Klasse ist die einfachste M?glichkeit, Threads zu implementieren. Schreiben Sie einfach die Ausführungsmethode der Thread-Klasse über die vom JDK bereitgestellte Thread-Klasse um. Wenn der Thread dann gestartet wird, wird der Inhalt des Ausführungsmethodenk?rpers ausgeführt. Der Code lautet wie folgt: package com.kingh.thread.create;
/**
* 繼承Thread類的方式創(chuàng)建線程
*
* @author <a href="https://blog.csdn.net/king_kgh>Kingh</a>
* @version 1.0
* @date 2019/3/13 19:19
*/
public class CreateThreadDemo1 extends Thread {
public CreateThreadDemo1() {
// 設(shè)置當前線程的名字
this.setName("MyThread");
}
@Override
public void run() {
// 每隔1s中輸出一次當前線程的名字
while (true) {
// 輸出線程的名字,與主線程名稱相區(qū)分
printThreadInfo();
try {
// 線程休眠一秒
Thread.sleep(1000);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
public static void main(String[] args) throws Exception {
// 注意這里,要調(diào)用start方法才能啟動線程,不能調(diào)用run方法
new CreateThreadDemo1().start();
// 演示主線程繼續(xù)向下執(zhí)行
while (true) {
printThreadInfo();
Thread.sleep(1000);
}
}
/**
* 輸出當前線程的信息
*/
private static void printThreadInfo() {
System.out.println("當前運行的線程名為: " + Thread.currentThread().getName());
}
}
Die laufenden Ergebnisse sind wie folgt
當前運行的線程名為: main 當前運行的線程名為: MyThread 當前運行的線程名為: main 當前運行的線程名為: MyThread 當前運行的線程名為: MyThread 當前運行的線程名為: mainHier ist zu beachten, dass beim Starten eines Threads nicht die Ausführungsmethode der Thread-Klasse aufgerufen wird, sondern die Startmethode der Thread-Klasse angerufen. K?nnen wir also die run-Methode aufrufen? Die Antwort lautet ?Ja“, da die Ausführungsmethode eine ?ffentlich deklarierte Methode ist, sodass wir sie aufrufen k?nnen. Wenn wir jedoch die Ausführungsmethode aufrufen, wird diese Methode als normale Methode aufgerufen und startet den Thread nicht. Dabei wird tats?chlich das Vorlagenmethodenmuster im Entwurfsmuster verwendet. Die Thread-Klasse dient als Vorlage, und die Ausführungsmethode ?ndert sich, sodass sie in einer Unterklasse implementiert wird.
1. Erstellen Sie mehrere ThreadsIm obigen Beispiel gibt es zus?tzlich zu dem einen Thread, den wir erstellt haben, tats?chlich einen Hauptthread, der auch ausgeführt wird. Gibt es neben diesen beiden Threads tats?chlich noch andere Threads, die ausgeführt werden? Beispielsweise wird der ?Garbage-Collection-Thread“, den wir nicht sehen k?nnen, ebenfalls stillschweigend ausgeführt. Hier berücksichtigen wir nicht, wie viele Threads ausgeführt werden. Wir haben oben selbst einen Thread erstellt. K?nnen wir also ein paar weitere erstellen und diese gemeinsam ausführen? Eine Thread-Klasse ist ein Thread-Objekt. Erstellen Sie daher mehrere Thread-Klassen und rufen Sie deren Startmethode auf, um mehrere Threads zu starten. Der Code lautet wie folgt
package com.kingh.thread.create; /** * 創(chuàng)建多個線程同時執(zhí)行 * * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a> * @version 1.0 * @date 2019/3/18 9:46 */ public class CreateMultiThreadDemo2 extends Thread { public CreateMultiThreadDemo2(String name) { // 設(shè)置當前線程的名字 this.setName(name); } @Override public void run() { // 每隔1s中輸出一次當前線程的名字 while (true) { // 輸出線程的名字,與主線程名稱相區(qū)分 printThreadInfo(); try { // 線程休眠一秒 Thread.sleep(1000); } catch (Exception e) { throw new RuntimeException(e); } } } public static void main(String[] args) throws Exception { // 注意這里,要調(diào)用start方法才能啟動線程,不能調(diào)用run方法 new CreateMultiThreadDemo2("MyThread-01").start(); // 創(chuàng)建多個線程實例,同時執(zhí)行 new CreateMultiThreadDemo2("MyThread-02").start(); // 演示主線程繼續(xù)向下執(zhí)行 while (true) { printThreadInfo(); Thread.sleep(1000); } } /** * 輸出當前線程的信息 */ private static void printThreadInfo() { System.out.println("當前運行的線程名為: " + Thread.currentThread().getName()); } }
Die laufenden Ergebnisse sind wie folgt
2. Geben Sie den Thread-Namen an
Sie k?nnen sehen, dass durch das Erstellen mehrerer Thread-Klassen und den Aufruf ihrer Startmethode mehrere Threads gestartet werden. Jeder Thread hat seinen eigenen Namen. Im obigen Code werden den erstellten Threads die Namen MyThread-01 und MyThread-02 zugewiesen. Anschlie?end wird der Thread-Name dem Thread-Namen zugewiesen, indem die setName-Methode der übergeordneten Klasse aufgerufen wird Konstruktormethode. Wenn Sie keinen Thread-Namen angeben, gibt das System standardm??ig den Thread-Namen an und die Benennungsregel hat die Form Thread-N. Um die Fehlerbehebung zu erleichtern, wird jedoch empfohlen, beim Erstellen eines Threads einen sinnvollen Thread-Namen anzugeben. Der folgende Code verwendet nicht den Thread-Namen當前運行的線程名為: main
當前運行的線程名為: MyThread-02
當前運行的線程名為: MyThread-01
當前運行的線程名為: main
當前運行的線程名為: MyThread-01
當前運行的線程名為: MyThread-02
當前運行的線程名為: main
Das laufende Ergebnis lautet wie folgt:package com.kingh.thread.create;
/**
* 創(chuàng)建多個線程同時執(zhí)行,使用系統(tǒng)默認線程名
*
* @author <a href="https://blog.csdn.net/king_kgh>Kingh</a>
* @version 1.0
* @date 2019/3/18 9:46
*/
public class CreateMultiThreadDemo3 extends Thread {
@Override
public void run() {
// 每隔1s中輸出一次當前線程的名字
while (true) {
// 輸出線程的名字,與主線程名稱相區(qū)分
printThreadInfo();
try {
// 線程休眠一秒
Thread.sleep(1000);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
public static void main(String[] args) throws Exception {
// 注意這里,要調(diào)用start方法才能啟動線程,不能調(diào)用run方法
new CreateMultiThreadDemo3().start();
// 創(chuàng)建多個線程實例,同時執(zhí)行
new CreateMultiThreadDemo3().start();
// 演示主線程繼續(xù)向下執(zhí)行
while (true) {
printThreadInfo();
Thread.sleep(1000);
}
}
/**
* 輸出當前線程的信息
*/
private static void printThreadInfo() {
System.out.println("當前運行的線程名為: " + Thread.currentThread().getName());
}
}
Zweiter Weg: Implementieren Sie die Runnable-SchnittstelleDie Implementierung der Runnable-Schnittstelle ist auch eine g?ngige Methode zum Erstellen von Threads. Die Verwendung von Schnittstellen kann die Kopplung in unseren Programmen reduzieren. In der Runnable-Schnittstelle ist nur eine Methode definiert, die ausgeführt wird. Werfen wir einen Blick auf den Code der Runnable-Schnittstelle. 當前運行的線程名為: main
當前運行的線程名為: Thread-1
當前運行的線程名為: Thread-0
當前運行的線程名為: main
當前運行的線程名為: Thread-1
當前運行的線程名為: Thread-0
Tats?chlich ist Runnable eine Thread-Aufgabe und Thread-Steuerung. Dies ist die oben erw?hnte Entkopplung. Wenn wir einen Thread implementieren m?chten, k?nnen wir die Thread-Klasse verwenden. Die von der Thread-Klasse auszuführenden Aufgaben k?nnen von Klassen verarbeitet werden, die die Runnable-Schnittstelle implementieren.
Runnable ist eine @FunctionalInterface-Funktionsschnittstelle, was bedeutet, dass Sie die von JDK8 bereitgestellte Lambda-Methode verwenden k?nnen, um Thread-Aufgaben zu erstellen. Der folgende Code zeigt den Lesern, wie sie verwendet werden.
Die Schritte zur Verwendung von Runnable zur Implementierung des obigen Beispiels sind wie folgt:
- 定義一個類實現(xiàn)Runnable接口,作為線程任務(wù)類
- 重寫run方法,并實現(xiàn)方法體,方法體的代碼就是線程所執(zhí)行的代碼
- 定義一個可以運行的類,并在main方法中創(chuàng)建線程任務(wù)類
- 創(chuàng)建Thread類,并將線程任務(wù)類做為Thread類的構(gòu)造方法傳入
- 啟動線程
1. 創(chuàng)建線程任務(wù)
線程任務(wù)就是線程要做的事情,這里我們讓這個線程每隔1s中打印自己的名字
package com.kingh.thread.create; /** * 線程任務(wù) * * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a> * @version 1.0 * @date 2019/3/18 10:04 */ public class CreateThreadDemo4_Task implements Runnable { @Override public void run() { // 每隔1s中輸出一次當前線程的名字 while (true) { // 輸出線程的名字,與主線程名稱相區(qū)分 printThreadInfo(); try { // 線程休眠一秒 Thread.sleep(1000); } catch (Exception e) { throw new RuntimeException(e); } } } /** * 輸出當前線程的信息 */ private static void printThreadInfo() { System.out.println("當前運行的線程名為: " + Thread.currentThread().getName()); } }2. 創(chuàng)建可運行類
在這里創(chuàng)建線程,并把任務(wù)交給線程處理,然后啟動線程。
package com.kingh.thread.create; /** * 創(chuàng)建線程 * * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a> * @version 1.0 * @date 2019/3/18 10:04 */ public class CreateThreadDemo4_Main { public static void main(String[] args) throws Exception { // 實例化線程任務(wù)類 CreateThreadDemo4_Task task = new CreateThreadDemo4_Task(); // 創(chuàng)建線程對象,并將線程任務(wù)類作為構(gòu)造方法參數(shù)傳入 new Thread(task).start(); // 主線程的任務(wù),為了演示多個線程一起執(zhí)行 while (true) { printThreadInfo(); Thread.sleep(1000); } } /** * 輸出當前線程的信息 */ private static void printThreadInfo() { System.out.println("當前運行的線程名為: " + Thread.currentThread().getName()); } }線程任務(wù)和線程的控制分離,那么一個線程任務(wù)可以提交給多個線程來執(zhí)行。這是很有用的,比如車站的售票窗口,每個窗口可以看做是一個線程,他們每個窗口做的事情都是一樣的,也就是售票。這樣我們程序在模擬現(xiàn)實的時候就可以定義一個售票任務(wù),讓多個窗口同時執(zhí)行這一個任務(wù)。那么如果要改動任務(wù)執(zhí)行計劃,只要修改線程任務(wù)類,所有的線程就都會按照修改后的來執(zhí)行。相比較繼承Thread類的方式來創(chuàng)建線程的方式,實現(xiàn)Runnable接口是更為常用的。
3. lambda方式創(chuàng)建線程任務(wù)
這里就是為了簡化內(nèi)部類的編寫,簡化了大量的模板代碼,顯得更加簡潔。如果讀者看不明白,可以讀完內(nèi)部類方式之后,回過來再看這段代碼。
package com.kingh.thread.create; /** * 創(chuàng)建線程with lambda * * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a> * @version 1.0 * @date 2019/3/18 10:04 */ public class CreateThreadDemo5_Lambda { public static void main(String[] args) throws Exception { // 使用lambda的形式實例化線程任務(wù)類 Runnable task = () -> { while (true) { // 輸出線程的名字 printThreadInfo(); } }; // 創(chuàng)建線程對象,并將線程任務(wù)類作為構(gòu)造方法參數(shù)傳入 new Thread(task).start(); // 主線程的任務(wù),為了演示多個線程一起執(zhí)行 while (true) { printThreadInfo(); Thread.sleep(1000); } } /** * 輸出當前線程的信息 */ private static void printThreadInfo() { System.out.println("當前運行的線程名為: " + Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (Exception e) { throw new RuntimeException(e); } } }這并不是一種新的實現(xiàn)線程的方式,只是另外的一種寫法。比如有些情況我們的線程就想執(zhí)行一次,以后就用不到了。那么像上面兩種方式(繼承Thread類和實現(xiàn)Runnable接口)都還要再定義一個類,顯得比較麻煩,我們就可以通過匿名內(nèi)部類的方式來實現(xiàn)。使用內(nèi)部類實現(xiàn)依然有兩種,分別是繼承Thread類和實現(xiàn)Runnable接口。代碼如下:
package com.kingh.thread.create; /** * 匿名內(nèi)部類的方式創(chuàng)建線程 * * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a> * @version 1.0 * @date 2019/3/18 10:04 */ public class CreateThreadDemo6_Anonymous { public static void main(String[] args) { // 基于子類的方式 new Thread() { @Override public void run() { while (true) { printThreadInfo(); } } }.start(); // 基于接口的實現(xiàn) new Thread(new Runnable() { @Override public void run() { while (true) { printThreadInfo(); } } }).start(); } /** * 輸出當前線程的信息 */ private static void printThreadInfo() { System.out.println("當前運行的線程名為: " + Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (Exception e) { throw new RuntimeException(e); } } }可以想象一下,我能不能既基于接口,又基于子類呢?像下面的代碼會執(zhí)行出什么樣子呢?
package com.kingh.thread.create; /** * 匿名內(nèi)部類的方式創(chuàng)建線程 * * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a> * @version 1.0 * @date 2019/3/18 10:04 */ public class CreateThreadDemo7_Anonymous { public static void main(String[] args) { // 基于子類和接口的方式 new Thread(new Runnable() { @Override public void run() { while (true) { printInfo("interface"); } } }) { @Override public void run() { while (true) { printInfo("sub class"); } } }.start(); } /** * 輸出當前線程的信息 */ private static void printInfo(String text) { System.out.println(text); try { Thread.sleep(1000); } catch (Exception e) { throw new RuntimeException(e); } } }運行結(jié)果如下:
sub class sub class我們可以看到,其實是基于子類的執(zhí)行了,為什么呢,其實很簡單,我們先來看一下為什么不基于子類的時候Runnable的run方法可以執(zhí)行。這個要從Thread的源碼看起,下面是我截取的代碼片段。
public Thread(Runnable target) init(null, target, "Thread-" + nextThreadNum(), 0); } private void init(ThreadGroup g, Runnable target, String name, long stackSize) { init(g, target, name, stackSize, null, true); } private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc, boolean inheritThreadLocals) { if (name == null) { throw new NullPointerException("name cannot be null"); } this.name = name; Thread parent = currentThread(); SecurityManager security = System.getSecurityManager(); if (g == null) { /* Determine if it's an applet or not */ /* If there is a security manager, ask the security manager what to do. */ if (security != null) { g = security.getThreadGroup(); } /* If the security doesn't have a strong opinion of the matter use the parent thread group. */ if (g == null) { g = parent.getThreadGroup(); } } /* checkAccess regardless of whether or not threadgroup is explicitly passed in. */ g.checkAccess(); /* * Do we have the required permissions? */ if (security != null) { if (isCCLOverridden(getClass())) { security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION); } } g.addUnstarted(); this.group = g; this.daemon = parent.isDaemon(); this.priority = parent.getPriority(); if (security == null || isCCLOverridden(parent.getClass())) this.contextClassLoader = parent.getContextClassLoader(); else this.contextClassLoader = parent.contextClassLoader; this.inheritedAccessControlContext = acc != null ? acc : AccessController.getContext(); this.target = target; // 注意這里 setPriority(priority); if (inheritThreadLocals && parent.inheritableThreadLocals != null) this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals); /* Stash the specified stack size in case the VM cares */ this.stackSize = stackSize; /* Set thread ID */ tid = nextThreadID(); }其實上面的眾多代碼就是為了表現(xiàn) this.target = target 那么target是什么呢,是Thread類的成員變量。那么在什么地方用到了target呢?下面是run方法的內(nèi)容。
@Override public void run() { if (target != null) { target.run(); } }我們可以看到,如果通過上面的構(gòu)造方法傳入target,那么就會執(zhí)行target中的run方法??赡苡信笥丫蜁柫?,我們同時繼承Thread類和實現(xiàn)Runnable接口,target不為空,那么為何不執(zhí)行target的run呢。不要忘記了,我們在子類中已經(jīng)重寫了Thread類的run方法,因此run方法已經(jīng)不在是我們看到的這樣了。那當然也就不回執(zhí)行target的run方法。
lambda 方式改造
剛才使用匿名內(nèi)部類,會發(fā)現(xiàn)代碼還是比較冗余的,lambda可以大大簡化代碼的編寫。用lambda來改寫上面的基于接口的形式的代碼,如下
// 使用lambda的形式 new Thread(() -> { while (true) { printThreadInfo(); } }).start(); // 對比不使用lambda的形式 new Thread(new Runnable() { @Override public void run() { while (true) { printThreadInfo(); } } }).start();定時器可以說是一種基于線程的一個工具類,可以定時的來執(zhí)行某個任務(wù)。在應(yīng)用中經(jīng)常需要定期執(zhí)行一些操作,比如要在凌晨的時候匯總一些數(shù)據(jù),比如要每隔10分鐘抓取一次某個網(wǎng)站上的數(shù)據(jù)等等,總之計時器無處不在。
在Java中實現(xiàn)定時任務(wù)有很多種方式,JDK提供了Timer類來幫助開發(fā)者創(chuàng)建定時任務(wù),另外也有很多的第三方框架提供了對定時任務(wù)的支持,比如Spring的schedule以及著名的quartz等等。因為Spring和quartz實現(xiàn)都比較重,依賴其他的包,上手稍微有些難度,不在本篇博客的討論范圍之內(nèi),這里就看一下JDK所給我們提供的API來實現(xiàn)定時任務(wù)。
1. 指定時間點執(zhí)行
package com.kingh.thread.create; import java.text.SimpleDateFormat; import java.util.Timer; import java.util.TimerTask; /** * 定時任務(wù) * * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a> * @version 1.0 * @date 2019/3/18 10:04 */ public class CreateThreadDemo9_Timer { private static final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); public static void main(String[] args) throws Exception { // 創(chuàng)建定時器 Timer timer = new Timer(); // 提交計劃任務(wù) timer.schedule(new TimerTask() { @Override public void run() { System.out.println("定時任務(wù)執(zhí)行了..."); } }, format.parse("2017-10-11 22:00:00")); } }2.間隔時間重復執(zhí)行
package com.kingh.thread.create; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Timer; import java.util.TimerTask; /** * 定時任務(wù) * * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a> * @version 1.0 * @date 2019/3/18 10:04 */ public class CreateThreadDemo10_Timer { public static void main(String[] args){ // 創(chuàng)建定時器 Timer timer = new Timer(); // 提交計劃任務(wù) timer.schedule(new TimerTask() { @Override public void run() { System.out.println("定時任務(wù)執(zhí)行了..."); } }, new Date(), 1000); } }關(guān)于Spring的定時任務(wù),可以參考 《Spring計劃任務(wù)》
我們發(fā)現(xiàn)上面提到的不管是繼承Thread類還是實現(xiàn)Runnable接口,發(fā)現(xiàn)有兩個問題,第一個是無法拋出更多的異常,第二個是線程執(zhí)行完畢之后并無法獲得線程的返回值。那么下面的這種實現(xiàn)方式就可以完成我們的需求。這種方式的實現(xiàn)就是我們后面要詳細介紹的Future模式,只是在jdk5的時候,官方給我們提供了可用的API,我們可以直接使用。但是使用這種方式創(chuàng)建線程比上面兩種方式要復雜一些,步驟如下。
創(chuàng)建一個類實現(xiàn)Callable接口,實現(xiàn)call方法。這個接口類似于Runnable接口,但比Runnable接口更加強大,增加了異常和返回值。
創(chuàng)建一個FutureTask,指定Callable對象,做為線程任務(wù)。
創(chuàng)建線程,指定線程任務(wù)。
啟動線程
代碼如下:
package com.kingh.thread.create; import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; /** * 帶返回值的方式 * * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a> * @version 1.0 * @date 2019/3/18 10:04 */ public class CreateThreadDemo11_Callable { public static void main(String[] args) throws Exception { // 創(chuàng)建線程任務(wù) Callable<Integer> call = () -> { System.out.println("線程任務(wù)開始執(zhí)行了...."); Thread.sleep(2000); return 1; }; // 將任務(wù)封裝為FutureTask FutureTask<Integer> task = new FutureTask<>(call); // 開啟線程,執(zhí)行線程任務(wù) new Thread(task).start(); // ==================== // 這里是在線程啟動之后,線程結(jié)果返回之前 System.out.println("這里可以為所欲為...."); // ==================== // 為所欲為完畢之后,拿到線程的執(zhí)行結(jié)果 Integer result = task.get(); System.out.println("主線程中拿到異步任務(wù)執(zhí)行的結(jié)果為:" + result); } }執(zhí)行結(jié)果如下:
這里可以為所欲為.... 線程任務(wù)開始執(zhí)行了.... 主線程中拿到異步任務(wù)執(zhí)行的結(jié)果為:1Callable中可以通過范型參數(shù)來指定線程的返回值類型。通過FutureTask的get方法拿到線程的返回值。
我們知道,線程和數(shù)據(jù)庫連接這些資源都是非常寶貴的資源。那么每次需要的時候創(chuàng)建,不需要的時候銷毀,是非常浪費資源的。那么我們就可以使用緩存的策略,也就是使用線程池。當然了,線程池也不需要我們來實現(xiàn),jdk的官方也給我們提供了API。
代碼如下:
package com.kingh.thread.create; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * 線程池 * * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a> * @version 1.0 * @date 2019/3/18 10:04 */ public class CreateThreadDemo12_ThreadPool { public static void main(String[] args) throws Exception { // 創(chuàng)建固定大小的線程池 ExecutorService threadPool = Executors.newFixedThreadPool(10); while (true) { // 提交多個線程任務(wù),并執(zhí)行 threadPool.execute(new Runnable() { @Override public void run() { printThreadInfo(); } }); } } /** * 輸出當前線程的信息 */ private static void printThreadInfo() { System.out.println("當前運行的線程名為: " + Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (Exception e) { throw new RuntimeException(e); } } }執(zhí)行結(jié)果如下:
當前運行的線程名為: pool-1-thread-1 當前運行的線程名為: pool-1-thread-2 當前運行的線程名為: pool-1-thread-4 當前運行的線程名為: pool-1-thread-3 當前運行的線程名為: pool-1-thread-7 當前運行的線程名為: pool-1-thread-8 當前運行的線程名為: pool-1-thread-9 當前運行的線程名為: pool-1-thread-6 當前運行的線程名為: pool-1-thread-5 當前運行的線程名為: pool-1-thread-10線程池的內(nèi)容還有非常多,這里不再詳細地講解。
更多編程相關(guān)知識,請訪問:編程教學!!
Das obige ist der detaillierte Inhalt vonMehrere M?glichkeiten, Multithreading in Java zu implementieren. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Hei?e KI -Werkzeuge

Undress AI Tool
Ausziehbilder kostenlos

Undresser.AI Undress
KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover
Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Clothoff.io
KI-Kleiderentferner

Video Face Swap
Tauschen Sie Gesichter in jedem Video mühelos mit unserem v?llig kostenlosen KI-Gesichtstausch-Tool aus!

Hei?er Artikel

Hei?e Werkzeuge

Notepad++7.3.1
Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version
Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1
Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6
Visuelle Webentwicklungstools

SublimeText3 Mac-Version
Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

Um JDBC -Transaktionen korrekt zu verarbeiten, müssen Sie zun?chst den automatischen Komiti -Modus ausschalten und dann mehrere Vorg?nge ausführen und schlie?lich entsprechend den Ergebnissen festlegen oder rollen. 1. Nennen Sie Conn.SetAutoCommit (False), um die Transaktion zu starten. 2. Führen Sie mehrere SQL -Operationen aus, z. B. einfügen und aktualisieren. 3. Rufen Sie Conn.Commit () an, wenn alle Vorg?nge erfolgreich sind, und rufen Sie Conn.Rollback () auf, wenn eine Ausnahme auftritt, um die Datenkonsistenz zu gew?hrleisten. Gleichzeitig sollten Try-with-Ressourcen verwendet werden, um Ressourcen zu verwalten, Ausnahmen ordnungsgem?? zu behandeln und Verbindungen zu schlie?en, um Verbindungsleckage zu vermeiden. Darüber hinaus wird empfohlen, Verbindungspools zu verwenden und Save -Punkte zu setzen, um teilweise Rollback zu erreichen und Transaktionen so kurz wie m?glich zu halten, um die Leistung zu verbessern.

Verwenden Sie Klassen im Java.Time -Paket, um das alte Datum und die Kalenderklassen zu ersetzen. 2. Erhalten Sie das aktuelle Datum und die aktuelle Uhrzeit durch LocalDate, LocalDatetime und Local Time; 3. Erstellen Sie ein bestimmtes Datum und eine bestimmte Uhrzeit mit der von () Methode; 4.. Verwenden Sie die Plus/Minus -Methode, um die Zeit nicht zu erh?hen und zu verkürzen. 5. Verwenden Sie ZonedDatetime und zoneId, um die Zeitzone zu verarbeiten. 6. Format und analysieren Sie Datumszeichenfolgen über DateTimeFormatter; 7. Verwenden Sie sofortige, um bei Bedarf mit den alten Datumstypen kompatibel zu sein. Die Verarbeitung der Datum in der modernen Java sollte der Verwendung von Java.Timeapi vorrangig machen, was klare, unver?nderliche und linear ist

Pre-Formancetartuptimemoryusage, QuarkusandmicronautleadduToCompile-Time-foringandgraalvSupport, WithQuarkusofttenperformLightBetterin serverloser Szenarien.2. Thyvelopecosystem,

Die Müllsammlung von Java (GC) ist ein Mechanismus, der automatisch den Speicher verwaltet, der das Risiko eines Speicherlecks verringert, indem unerreichbare Objekte zurückgeführt werden. 1.GC beurteilt die Zug?nglichkeit des Objekts aus dem Stammobjekt (z. B. Stapelvariablen, aktive Threads, statische Felder usw.) und nicht erreichbare Objekte als Müll markiert. 2. Basierend auf dem markierten Algorithmus markieren Sie alle erreichbaren Objekte und l?schen Sie nicht markierte Objekte. 3.. Verfolgen Sie eine Generationskollektionsstrategie: Die neue Generation (Eden, S0, S1) führt h?ufig MollGC aus; Die ?lteren Menschen erzielen weniger, dauert jedoch l?nger, um MajorGC durchzuführen. MetaPace speichert Klassenmetadaten. 4. JVM bietet eine Vielzahl von GC -Ger?ten: SerialGC ist für kleine Anwendungen geeignet; ParallelgC verbessert den Durchsatz; CMS reduziert sich

HTTP-Protokoll Middleware in Go kann Anforderungsmethoden, Pfade, Client-IP und zeitaufw?ndiges Aufzeichnen aufzeichnen. 1. Verwenden Sie http.Handlerfunc, um den Prozessor zu wickeln, 2. Nehmen Sie die Startzeit und die Endzeit vor und nach dem Aufrufen als n?chstes auf. Der vollst?ndige Beispielcode wurde überprüft, um auszuführen und eignet sich zum Starten eines kleinen und mittelgro?en Projekts. Zu den Erweiterungsvorschl?gen geh?ren das Erfassen von Statuscodes, die Unterstützung von JSON -Protokollen und die Nachverfolgung von ID -IDs.

Durch die Auswahl des richtigen HTMlinput -Typs kann die Datengenauigkeit verbessert, die Benutzererfahrung verbessert und die Benutzerfreundlichkeit verbessert werden. 1. W?hlen Sie die entsprechenden Eingabetypen gem?? dem Datentyp aus, z. B. Text, E -Mail, Tel, Nummer und Datum, die automatisch überprüft und an die Tastatur anpassen k?nnen. 2. Verwenden Sie HTML5, um neue Typen wie URL, Farbe, Reichweite und Suche hinzuzufügen, die eine intuitivere Interaktionsmethode bieten k?nnen. 3.. Verwenden Sie Platzhalter und erforderliche Attribute, um die Effizienz und Genauigkeit der Formulierung zu verbessern. Es sollte jedoch beachtet werden, dass der Platzhalter das Etikett nicht ersetzen kann.

GradleStheBetterChoiceFormostnewProjectsDuetoitSuperiorFlexibilit?t, Leistung und ModerntoolingSupport.1.GRADLE'SGROOVY/KOTLINDSLISMORECONCISEANDEIPRESSIVETHANMANMANBOSEXML.2.GRAGRECONCISEANDEPRPRESSIVETHANMAVENSVOSEXML.2.

Auf Defer wird verwendet, um bestimmte Vorg?nge auszuführen, bevor die Funktion zurückgibt, z. B. die Reinigungsressourcen. Die Parameter werden sofort bewertet, wenn sie aufgeschoben werden, und die Funktionen werden in der Reihenfolge von Last-In-First-Out (LIFO) ausgeführt. 1. Mehrere Defers werden in umgekehrter Reihenfolge der Erkl?rungen ausgeführt. 2. h?ufig für die sichere Reinigung wie das Schlie?en von Dateien verwendet; 3. Der benannte Rückgabewert kann ge?ndert werden; V. 5. Vermeiden Sie den Missbrauch von Verschiebungen in Schleifen, um Ressourcenleckage zu verhindern. Die korrekte Verwendung kann die Sicherheit und die Lesbarkeit der Code verbessern.
