Extremely Fast and Efficient: Mastering the Art of Java Coroutine Programming

insert image description here

Many Internet companies in China use the Go language. One of the important reasons is the superior performance of the Go language.

There are many deficiencies in the traditional thread concurrent programming method. In a high-concurrency environment, operations such as context switching, thread creation and destruction caused by threads will bring a lot of performance and resource consumption. In Java programming, we can also choose more Efficient concurrent programming method: Java coroutine coroutine.

  • Save memory: A coroutine takes up less memory than a thread because it does not need to maintain its own stack space and register state.
  • Improved performance: Coroutines are less expensive than thread switching because they don't need to save and restore the thread's state.
  • Simplified programming: Coroutines are easier to write and understand than threads, because it can use synchronous coding style to implement asynchronous logic.

Implementation of coroutines in Java

Java coroutine, also known as "lightweight thread" or "fiber (Fiber)", is a coroutine technology based on user mode.

Java coroutines switch between coroutines through cooperative scheduling. Each coroutine has its own stack space. Switching between coroutines does not require thread switching. It only needs to realize the stack space of coroutines in user mode. switch. In this way, the switching overhead of the Java coroutine is smaller, and the resources of the computer can be better utilized.

Java does not have built-in coroutine support, which is currently mainly implemented through three-party libraries:

  • Quasar: implements coroutines through Fiber, with better performance but only supports JDK1.8 and above
  • Kilim: Implement coroutines through Yield, poor performance but support JDK1.5 and above

Next, we choose Quasar to implement the coroutine, and use Fiber to represent a coroutine.

the code


public class CoroutineExample {
    
    
    public static void main(String[] args) {
    
    
        Fiber<Void> fiber1 = new Fiber<Void>() {
    
    
            public Void run() throws SuspendExecution {
    
    
                System.out.println("Fiber 1 start");
                fiber2.resume(); // 切换到Fiber 2
                System.out.println("Fiber 1 resume");
                Fiber.yield();   // Fiber 1 等待
                System.out.println("Fiber 1 end");
            }
        };

        Fiber<Void> fiber2 = new Fiber<Void>() {
    
    
            public Void run() throws SuspendExecution {
    
    
                System.out.println("Fiber 2 start");
                fiber3.resume(); // 切换到Fiber 3
                System.out.println("Fiber 2 resume");  
                fiber1.resume(); // 切回到Fiber 1
                Fiber.yield();   // Fiber 2 等待
                System.out.println("Fiber 2 end");
            }
        };

        Fiber<Void> fiber3 = new Fiber<Void>() {
    
    
            public Void run() throws SuspendExecution {
    
    
                System.out.println("Fiber 3 start");
                fiber2.resume();  // 切换回Fiber 2
                System.out.println("Fiber 3 end");
            }
        };

        fiber1.start();  // 启动Fiber 1
    }
}

运行结果:
Fiber 1 start
Fiber 2 start
Fiber 3 start
Fiber 3 end
Fiber 2 resume
Fiber 1 resume
Fiber 1 end
Fiber 2 end

It can be seen that the three coroutines realized cooperation by switching each other, and finally completed the execution.

How does Quasar implement Fiber?

Quasar uses Java Agent for bytecode implantation, which supports the implementation of coroutines at the JVM level.

Quasar Fiber uses bytecode modification technology to weave necessary context saving/recovery codes when compiling or loading, and pauses by throwing exceptions. When resuming, it restores the jvm method call stack and Local variables, Quasar Fiber provides the corresponding Java class library to implement, which is somewhat intrusive to the application (very small)

Quasar Fiber mainly consists of Instrument + Continuation + Scheduler

  • Instrument does some code implantation, such as saving/restoring the context before and after park, etc.;
  • Continuation saves information about method calls, such as local variables, references, etc.;
  • Scheduler scheduler, responsible for assigning fibers to specific operating system threads for execution

Summarize

Coroutines and threads are closely related. Coroutines can be considered as code blocks running on threads. The suspension operation provided by coroutines will suspend the execution of coroutines without causing thread blocking.

Coroutines are also a lightweight resource. Even if thousands of coroutines are created, it is not a big burden on the system, but if thousands of threads are created in the program, the system will really be under great pressure. . It can be said that the design of coroutines has greatly improved the utilization rate of threads.

In actual cases, the application of coroutines in Go language is relatively mature, and the coroutines in Java are not very stable at present. The key point is the lack of verification of large-scale projects. It can be said that the design of Java coroutines still has a long way to go. Way to go.

Well, the above is all the content shared today, enjoy welcome to complain and communicate~~ WeChat public account [AI Blackboard News]

Guess you like

Origin blog.csdn.net/lomodays207/article/details/130394659