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è)置當(dāng)前線程的名字
this.setName("MyThread");
}
@Override
public void run() {
// 每隔1s中輸出一次當(dāng)前線程的名字
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方法才能啟動(dòng)線程,不能調(diào)用run方法
new CreateThreadDemo1().start();
// 演示主線程繼續(xù)向下執(zhí)行
while (true) {
printThreadInfo();
Thread.sleep(1000);
}
}
/**
* 輸出當(dāng)前線程的信息
*/
private static void printThreadInfo() {
System.out.println("當(dāng)前運(yùn)行的線程名為: " + Thread.currentThread().getName());
}
}
Die laufenden Ergebnisse sind wie folgt
當(dāng)前運(yùn)行的線程名為: main 當(dāng)前運(yùn)行的線程名為: MyThread 當(dāng)前運(yùn)行的線程名為: main 當(dāng)前運(yùn)行的線程名為: MyThread 當(dāng)前運(yùn)行的線程名為: MyThread 當(dāng)前運(yùn)行的線程名為: 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)建多個(gè)線程同時(shí)執(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è)置當(dāng)前線程的名字 this.setName(name); } @Override public void run() { // 每隔1s中輸出一次當(dāng)前線程的名字 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方法才能啟動(dòng)線程,不能調(diào)用run方法 new CreateMultiThreadDemo2("MyThread-01").start(); // 創(chuàng)建多個(gè)線程實(shí)例,同時(shí)執(zhí)行 new CreateMultiThreadDemo2("MyThread-02").start(); // 演示主線程繼續(xù)向下執(zhí)行 while (true) { printThreadInfo(); Thread.sleep(1000); } } /** * 輸出當(dāng)前線程的信息 */ private static void printThreadInfo() { System.out.println("當(dāng)前運(yùn)行的線程名為: " + 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當(dāng)前運(yùn)行的線程名為: main
當(dāng)前運(yùn)行的線程名為: MyThread-02
當(dāng)前運(yùn)行的線程名為: MyThread-01
當(dāng)前運(yùn)行的線程名為: main
當(dāng)前運(yùn)行的線程名為: MyThread-01
當(dāng)前運(yùn)行的線程名為: MyThread-02
當(dāng)前運(yùn)行的線程名為: main
Das laufende Ergebnis lautet wie folgt:package com.kingh.thread.create;
/**
* 創(chuàng)建多個(gè)線程同時(shí)執(zhí)行,使用系統(tǒng)默認(rèn)線程名
*
* @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中輸出一次當(dāng)前線程的名字
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方法才能啟動(dòng)線程,不能調(diào)用run方法
new CreateMultiThreadDemo3().start();
// 創(chuàng)建多個(gè)線程實(shí)例,同時(shí)執(zhí)行
new CreateMultiThreadDemo3().start();
// 演示主線程繼續(xù)向下執(zhí)行
while (true) {
printThreadInfo();
Thread.sleep(1000);
}
}
/**
* 輸出當(dāng)前線程的信息
*/
private static void printThreadInfo() {
System.out.println("當(dāng)前運(yùn)行的線程名為: " + 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. 當(dāng)前運(yùn)行的線程名為: main
當(dāng)前運(yùn)行的線程名為: Thread-1
當(dāng)前運(yùn)行的線程名為: Thread-0
當(dāng)前運(yùn)行的線程名為: main
當(dāng)前運(yùn)行的線程名為: Thread-1
當(dāng)前運(yùn)行的線程名為: 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:
- 定義一個(gè)類實(shí)現(xiàn)Runnable接口,作為線程任務(wù)類
- 重寫run方法,并實(shí)現(xiàn)方法體,方法體的代碼就是線程所執(zhí)行的代碼
- 定義一個(gè)可以運(yùn)行的類,并在main方法中創(chuàng)建線程任務(wù)類
- 創(chuàng)建Thread類,并將線程任務(wù)類做為Thread類的構(gòu)造方法傳入
- 啟動(dòng)線程
1. 創(chuàng)建線程任務(wù)
線程任務(wù)就是線程要做的事情,這里我們讓這個(gè)線程每隔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中輸出一次當(dāng)前線程的名字 while (true) { // 輸出線程的名字,與主線程名稱相區(qū)分 printThreadInfo(); try { // 線程休眠一秒 Thread.sleep(1000); } catch (Exception e) { throw new RuntimeException(e); } } } /** * 輸出當(dāng)前線程的信息 */ private static void printThreadInfo() { System.out.println("當(dāng)前運(yùn)行的線程名為: " + Thread.currentThread().getName()); } }2. 創(chuàng)建可運(yùn)行類
在這里創(chuàng)建線程,并把任務(wù)交給線程處理,然后啟動(dòng)線程。
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 { // 實(shí)例化線程任務(wù)類 CreateThreadDemo4_Task task = new CreateThreadDemo4_Task(); // 創(chuàng)建線程對(duì)象,并將線程任務(wù)類作為構(gòu)造方法參數(shù)傳入 new Thread(task).start(); // 主線程的任務(wù),為了演示多個(gè)線程一起執(zhí)行 while (true) { printThreadInfo(); Thread.sleep(1000); } } /** * 輸出當(dāng)前線程的信息 */ private static void printThreadInfo() { System.out.println("當(dāng)前運(yùn)行的線程名為: " + Thread.currentThread().getName()); } }線程任務(wù)和線程的控制分離,那么一個(gè)線程任務(wù)可以提交給多個(gè)線程來(lái)執(zhí)行。這是很有用的,比如車站的售票窗口,每個(gè)窗口可以看做是一個(gè)線程,他們每個(gè)窗口做的事情都是一樣的,也就是售票。這樣我們程序在模擬現(xiàn)實(shí)的時(shí)候就可以定義一個(gè)售票任務(wù),讓多個(gè)窗口同時(shí)執(zhí)行這一個(gè)任務(wù)。那么如果要改動(dòng)任務(wù)執(zhí)行計(jì)劃,只要修改線程任務(wù)類,所有的線程就都會(huì)按照修改后的來(lái)執(zhí)行。相比較繼承Thread類的方式來(lái)創(chuàng)建線程的方式,實(shí)現(xiàn)Runnable接口是更為常用的。
3. lambda方式創(chuàng)建線程任務(wù)
這里就是為了簡(jiǎn)化內(nèi)部類的編寫,簡(jiǎn)化了大量的模板代碼,顯得更加簡(jiǎn)潔。如果讀者看不明白,可以讀完內(nèi)部類方式之后,回過(guò)來(lá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的形式實(shí)例化線程任務(wù)類 Runnable task = () -> { while (true) { // 輸出線程的名字 printThreadInfo(); } }; // 創(chuàng)建線程對(duì)象,并將線程任務(wù)類作為構(gòu)造方法參數(shù)傳入 new Thread(task).start(); // 主線程的任務(wù),為了演示多個(gè)線程一起執(zhí)行 while (true) { printThreadInfo(); Thread.sleep(1000); } } /** * 輸出當(dāng)前線程的信息 */ private static void printThreadInfo() { System.out.println("當(dāng)前運(yùn)行的線程名為: " + Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (Exception e) { throw new RuntimeException(e); } } }這并不是一種新的實(shí)現(xiàn)線程的方式,只是另外的一種寫法。比如有些情況我們的線程就想執(zhí)行一次,以后就用不到了。那么像上面兩種方式(繼承Thread類和實(shí)現(xiàn)Runnable接口)都還要再定義一個(gè)類,顯得比較麻煩,我們就可以通過(guò)匿名內(nèi)部類的方式來(lái)實(shí)現(xiàn)。使用內(nèi)部類實(shí)現(xiàn)依然有兩種,分別是繼承Thread類和實(shí)現(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(); // 基于接口的實(shí)現(xiàn) new Thread(new Runnable() { @Override public void run() { while (true) { printThreadInfo(); } } }).start(); } /** * 輸出當(dāng)前線程的信息 */ private static void printThreadInfo() { System.out.println("當(dāng)前運(yùn)行的線程名為: " + Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (Exception e) { throw new RuntimeException(e); } } }可以想象一下,我能不能既基于接口,又基于子類呢?像下面的代碼會(huì)執(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(); } /** * 輸出當(dāng)前線程的信息 */ private static void printInfo(String text) { System.out.println(text); try { Thread.sleep(1000); } catch (Exception e) { throw new RuntimeException(e); } } }運(yùn)行結(jié)果如下:
sub class sub class我們可以看到,其實(shí)是基于子類的執(zhí)行了,為什么呢,其實(shí)很簡(jiǎn)單,我們先來(lái)看一下為什么不基于子類的時(shí)候Runnable的run方法可以執(zhí)行。這個(gè)要從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(); }其實(shí)上面的眾多代碼就是為了表現(xiàn) this.target = target 那么target是什么呢,是Thread類的成員變量。那么在什么地方用到了target呢?下面是run方法的內(nèi)容。
@Override public void run() { if (target != null) { target.run(); } }我們可以看到,如果通過(guò)上面的構(gòu)造方法傳入target,那么就會(huì)執(zhí)行target中的run方法??赡苡信笥丫蜁?huì)問(wèn)了,我們同時(shí)繼承Thread類和實(shí)現(xiàn)Runnable接口,target不為空,那么為何不執(zhí)行target的run呢。不要忘記了,我們?cè)谧宇愔幸呀?jīng)重寫了Thread類的run方法,因此run方法已經(jīng)不在是我們看到的這樣了。那當(dāng)然也就不回執(zhí)行target的run方法。
lambda 方式改造
剛才使用匿名內(nèi)部類,會(huì)發(fā)現(xiàn)代碼還是比較冗余的,lambda可以大大簡(jiǎn)化代碼的編寫。用lambda來(lái)改寫上面的基于接口的形式的代碼,如下
// 使用lambda的形式 new Thread(() -> { while (true) { printThreadInfo(); } }).start(); // 對(duì)比不使用lambda的形式 new Thread(new Runnable() { @Override public void run() { while (true) { printThreadInfo(); } } }).start();定時(shí)器可以說(shuō)是一種基于線程的一個(gè)工具類,可以定時(shí)的來(lái)執(zhí)行某個(gè)任務(wù)。在應(yīng)用中經(jīng)常需要定期執(zhí)行一些操作,比如要在凌晨的時(shí)候匯總一些數(shù)據(jù),比如要每隔10分鐘抓取一次某個(gè)網(wǎng)站上的數(shù)據(jù)等等,總之計(jì)時(shí)器無(wú)處不在。
在Java中實(shí)現(xiàn)定時(shí)任務(wù)有很多種方式,JDK提供了Timer類來(lái)幫助開(kāi)發(fā)者創(chuàng)建定時(shí)任務(wù),另外也有很多的第三方框架提供了對(duì)定時(shí)任務(wù)的支持,比如Spring的schedule以及著名的quartz等等。因?yàn)镾pring和quartz實(shí)現(xiàn)都比較重,依賴其他的包,上手稍微有些難度,不在本篇博客的討論范圍之內(nèi),這里就看一下JDK所給我們提供的API來(lái)實(shí)現(xiàn)定時(shí)任務(wù)。
1. 指定時(shí)間點(diǎn)執(zhí)行
package com.kingh.thread.create; import java.text.SimpleDateFormat; import java.util.Timer; import java.util.TimerTask; /** * 定時(shí)任務(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)建定時(shí)器 Timer timer = new Timer(); // 提交計(jì)劃任務(wù) timer.schedule(new TimerTask() { @Override public void run() { System.out.println("定時(shí)任務(wù)執(zhí)行了..."); } }, format.parse("2017-10-11 22:00:00")); } }2.間隔時(shí)間重復(fù)執(zhí)行
package com.kingh.thread.create; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Timer; import java.util.TimerTask; /** * 定時(shí)任務(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)建定時(shí)器 Timer timer = new Timer(); // 提交計(jì)劃任務(wù) timer.schedule(new TimerTask() { @Override public void run() { System.out.println("定時(shí)任務(wù)執(zhí)行了..."); } }, new Date(), 1000); } }關(guān)于Spring的定時(shí)任務(wù),可以參考 《Spring計(jì)劃任務(wù)》
我們發(fā)現(xiàn)上面提到的不管是繼承Thread類還是實(shí)現(xiàn)Runnable接口,發(fā)現(xiàn)有兩個(gè)問(wèn)題,第一個(gè)是無(wú)法拋出更多的異常,第二個(gè)是線程執(zhí)行完畢之后并無(wú)法獲得線程的返回值。那么下面的這種實(shí)現(xiàn)方式就可以完成我們的需求。這種方式的實(shí)現(xiàn)就是我們后面要詳細(xì)介紹的Future模式,只是在jdk5的時(shí)候,官方給我們提供了可用的API,我們可以直接使用。但是使用這種方式創(chuàng)建線程比上面兩種方式要復(fù)雜一些,步驟如下。
創(chuàng)建一個(gè)類實(shí)現(xiàn)Callable接口,實(shí)現(xiàn)call方法。這個(gè)接口類似于Runnable接口,但比Runnable接口更加強(qiáng)大,增加了異常和返回值。
創(chuàng)建一個(gè)FutureTask,指定Callable對(duì)象,做為線程任務(wù)。
創(chuàng)建線程,指定線程任務(wù)。
啟動(dòng)線程
代碼如下:
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ù)開(kāi)始執(zhí)行了...."); Thread.sleep(2000); return 1; }; // 將任務(wù)封裝為FutureTask FutureTask<Integer> task = new FutureTask<>(call); // 開(kāi)啟線程,執(zhí)行線程任務(wù) new Thread(task).start(); // ==================== // 這里是在線程啟動(dòng)之后,線程結(jié)果返回之前 System.out.println("這里可以為所欲為...."); // ==================== // 為所欲為完畢之后,拿到線程的執(zhí)行結(jié)果 Integer result = task.get(); System.out.println("主線程中拿到異步任務(wù)執(zhí)行的結(jié)果為:" + result); } }執(zhí)行結(jié)果如下:
這里可以為所欲為.... 線程任務(wù)開(kāi)始執(zhí)行了.... 主線程中拿到異步任務(wù)執(zhí)行的結(jié)果為:1Callable中可以通過(guò)范型參數(shù)來(lái)指定線程的返回值類型。通過(guò)FutureTask的get方法拿到線程的返回值。
我們知道,線程和數(shù)據(jù)庫(kù)連接這些資源都是非常寶貴的資源。那么每次需要的時(shí)候創(chuàng)建,不需要的時(shí)候銷毀,是非常浪費(fèi)資源的。那么我們就可以使用緩存的策略,也就是使用線程池。當(dāng)然了,線程池也不需要我們來(lái)實(shí)現(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) { // 提交多個(gè)線程任務(wù),并執(zhí)行 threadPool.execute(new Runnable() { @Override public void run() { printThreadInfo(); } }); } } /** * 輸出當(dāng)前線程的信息 */ private static void printThreadInfo() { System.out.println("當(dāng)前運(yùn)行的線程名為: " + Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (Exception e) { throw new RuntimeException(e); } } }執(zhí)行結(jié)果如下:
當(dāng)前運(yùn)行的線程名為: pool-1-thread-1 當(dāng)前運(yùn)行的線程名為: pool-1-thread-2 當(dāng)前運(yùn)行的線程名為: pool-1-thread-4 當(dāng)前運(yùn)行的線程名為: pool-1-thread-3 當(dāng)前運(yùn)行的線程名為: pool-1-thread-7 當(dāng)前運(yùn)行的線程名為: pool-1-thread-8 當(dāng)前運(yùn)行的線程名為: pool-1-thread-9 當(dāng)前運(yùn)行的線程名為: pool-1-thread-6 當(dāng)前運(yùn)行的線程名為: pool-1-thread-5 當(dāng)前運(yùn)行的線程名為: pool-1-thread-10線程池的內(nèi)容還有非常多,這里不再詳細(xì)地講解。
更多編程相關(guān)知識(shí),請(qǐng)?jiān)L問(wèn):編程教學(xué)!!
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

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.

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.
