Ways to implement multi-threading: 1. Inherit the Thread class, use the Thread class provided by JDK, and rewrite the run method of the Thread class; 2. Implement the Runnable interface, which is a "@FunctionalInterface" functional formula interface, which means that you can use the lambda method provided by JDK8 to create thread tasks; 3. Use internal classes; 4. Use timers; 5. Thread implementation with return values; 6. Implement multiple threads based on thread pools. thread.
The operating environment of this tutorial: windows7 system, java8 version, DELL G3 computer.
There are two main ways to implement multi-threading in form, one is to inherit the Thread class, and the other is to implement the Runnable interface. Essentially, the implementation method is to implement the thread task, and then start the thread to execute the thread task (the thread task here is actually the run method). The 6 types mentioned here are actually some modifications based on the above two types.
The following will introduce each of these 6 implementation methods one by one.
The first way: inherit the Thread class
Everything is an object, then threads are also objects, and objects should Its public characteristics can be extracted and encapsulated into classes, and multiple objects can be instantiated using classes. Then the first way to implement threads is to inherit the Thread class. Inheriting the Thread class is the simplest way to implement threads. Just rewrite the run method of the Thread class through the Thread class provided by the JDK. Then when the thread starts, the contents of the run method body will be executed. The code is as follows:
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()); } }
The running results are as follows
當前運行的線程名為: main 當前運行的線程名為: MyThread 當前運行的線程名為: main 當前運行的線程名為: MyThread 當前運行的線程名為: MyThread 當前運行的線程名為: main
It should be noted here that when starting a thread, the run method of the thread class is not called, but the start of the thread class is called. method. So can we call the run method? The answer is yes, because the run method is a publicly declared method, so we can call it, but if we call the run method, then this method will be called as an ordinary method and will not start the thread. This actually uses the template method pattern in the design pattern. The Thread class serves as the template, and the run method is changing, so it is implemented in a subclass.
1. Create multiple threads
In the above example, in addition to the one thread we created, there is actually a main thread Also executing. So besides these two threads, are there any other threads executing? In fact, there are. For example, the Garbage Collection Thread that we cannot see is also executing silently. Here we do not consider how many threads are executing. We have created a thread ourselves above, so can we create a few more and execute them together? The answer is yes. A Thread class is a thread object, so create several Thread classes and call their start method to start multiple threads. The code is as follows
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()); } }
The running result is as follows
當前運行的線程名為: main 當前運行的線程名為: MyThread-02 當前運行的線程名為: MyThread-01 當前運行的線程名為: main 當前運行的線程名為: MyThread-01 當前運行的線程名為: MyThread-02 當前運行的線程名為: main
2. Specify the thread name
You can see that by creating multiple Thread class, and its start method is called to start multiple threads. Each thread has its own name. In the above code, the names MyThread-01 and MyThread-02 are assigned to the created threads, and then the thread name is assigned to the thread name by calling the setName method of the parent class in the constructor method. If you do not specify a thread name, the system will specify the thread name by default, and the naming rule is in the form of Thread-N. However, in order to facilitate troubleshooting, it is recommended to specify a reasonable thread name when creating a thread. The following code is what it looks like without using the thread name
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()); } }
The running result is as follows:
當前運行的線程名為: main 當前運行的線程名為: Thread-1 當前運行的線程名為: Thread-0 當前運行的線程名為: main 當前運行的線程名為: Thread-1 當前運行的線程名為: Thread-0
The second way: implement the Runnable interface
Implementing the Runnable interface is also a common way to create threads. Using the interface can reduce the coupling of our programs. There is only one method defined in the Runnable interface, which is run. Let's take a look at the code of the Runnable interface.
package java.lang; @FunctionalInterface public interface Runnable { public abstract void run(); }
In fact, Runnable is a thread task. The thread task and thread control are separated. This is the decoupling mentioned above. If we want to implement a thread, we can use the Thread class. The tasks to be performed by the Thread class can be handled by classes that implement the Runnable interface. This is the essence of Runnable!
Runnable is a @FunctionalInterface functional interface, which means that you can use the lambda method provided by JDK8 to create thread tasks. The following code will show readers how to use it.
The steps to use Runnable to implement the above example are as follows:
- 定義一個類實現(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.間隔時間重復(fù)執(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)建線程比上面兩種方式要復(fù)雜一些,步驟如下。
創(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é)果為:1
Callable中可以通過范型參數(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)知識,請訪問:編程教學??!
The above is the detailed content of Several ways to implement multithreading in java. For more information, please follow other related articles on the PHP Chinese website!

Hot AI Tools

Undress AI Tool
Undress images for free

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics

The settings.json file is located in the user-level or workspace-level path and is used to customize VSCode settings. 1. User-level path: Windows is C:\Users\\AppData\Roaming\Code\User\settings.json, macOS is /Users//Library/ApplicationSupport/Code/User/settings.json, Linux is /home//.config/Code/User/settings.json; 2. Workspace-level path: .vscode/settings in the project root directory

Selecting the Java SpringBoot React technology stack can build stable and efficient full-stack web applications, suitable for small and medium-sized to large enterprise-level systems. 2. The backend uses SpringBoot to quickly build RESTfulAPI. The core components include SpringWeb, SpringDataJPA, SpringSecurity, Lombok and Swagger. The front-end separation is achieved through @RestController returning JSON data. 3. The front-end uses React (in conjunction with Vite or CreateReactApp) to develop a responsive interface, uses Axios to call the back-end API, and ReactRouter

To correctly handle JDBC transactions, you must first turn off the automatic commit mode, then perform multiple operations, and finally commit or rollback according to the results; 1. Call conn.setAutoCommit(false) to start the transaction; 2. Execute multiple SQL operations, such as INSERT and UPDATE; 3. Call conn.commit() if all operations are successful, and call conn.rollback() if an exception occurs to ensure data consistency; at the same time, try-with-resources should be used to manage resources, properly handle exceptions and close connections to avoid connection leakage; in addition, it is recommended to use connection pools and set save points to achieve partial rollback, and keep transactions as short as possible to improve performance.

Use performance analysis tools to locate bottlenecks, use VisualVM or JProfiler in the development and testing stage, and give priority to Async-Profiler in the production environment; 2. Reduce object creation, reuse objects, use StringBuilder to replace string splicing, and select appropriate GC strategies; 3. Optimize collection usage, select and preset initial capacity according to the scene; 4. Optimize concurrency, use concurrent collections, reduce lock granularity, and set thread pool reasonably; 5. Tune JVM parameters, set reasonable heap size and low-latency garbage collector and enable GC logs; 6. Avoid reflection at the code level, replace wrapper classes with basic types, delay initialization, and use final and static; 7. Continuous performance testing and monitoring, combined with JMH

itertools.combinations is used to generate all non-repetitive combinations (order irrelevant) that selects a specified number of elements from the iterable object. Its usage includes: 1. Select 2 element combinations from the list, such as ('A','B'), ('A','C'), etc., to avoid repeated order; 2. Take 3 character combinations of strings, such as "abc" and "abd", which are suitable for subsequence generation; 3. Find the combinations where the sum of two numbers is equal to the target value, such as 1 5=6, simplify the double loop logic; the difference between combinations and arrangement lies in whether the order is important, combinations regard AB and BA as the same, while permutations are regarded as different;

DependencyInjection(DI)isadesignpatternwhereobjectsreceivedependenciesexternally,promotingloosecouplingandeasiertestingthroughconstructor,setter,orfieldinjection.2.SpringFrameworkusesannotationslike@Component,@Service,and@AutowiredwithJava-basedconfi

fixture is a function used to provide preset environment or data for tests. 1. Use the @pytest.fixture decorator to define fixture; 2. Inject fixture in parameter form in the test function; 3. Execute setup before yield, and then teardown; 4. Control scope through scope parameters, such as function, module, etc.; 5. Place the shared fixture in conftest.py to achieve cross-file sharing, thereby improving the maintainability and reusability of tests.

JavaFlightRecorder(JFR)andJavaMissionControl(JMC)providedeep,low-overheadinsightsintoJavaapplicationperformance.1.JFRcollectsruntimedatalikeGCbehavior,threadactivity,CPUusage,andcustomeventswithlessthan2%overhead,writingittoa.jfrfile.2.EnableJFRatsta
