国内很多互联网公司都使用 Go 语言,其中一个很重要的原因就是 Go 语言优越的性能表现。
传统的线程并发编程方式存在着许多不足,在高并发环境下,线程引起的上下文切换、线程的创建和销毁等操作会带来很大的性能和资源消耗,在Java编程中我们也可以选择更加高效的并发编程方式:Java协程coroutine 。
- 节省内存:协程比线程占用的内存更少,因为它不需要维护自己的栈空间和寄存器状态。
- 提高性能:协程比线程切换的开销更小,因为它不需要保存和恢复线程的状态。
- 简化编程:协程比线程更容易编写和理解,因为它可以使用同步的编码风格来实现异步的逻辑。
Java中协程的实现
Java协程,又被称为“轻量级线程”或“纤程(Fiber)”,是一种基于用户态的协程技术。
Java协程通过协作式调度实现协程之间的切换,每一个协程都有自己的栈空间,协程之间的切换并不需要线程切换,只需要在用户态下实现协程栈空间的切换。这样一来,Java协程的切换开销更小,可以更好地利用计算机的资源。
Java没有内置的协程支持,目前主要通过三方库实现:
- Quasar: 通过Fiber实现协程,性能较好但只支持JDK1.8及以上
- Kilim: 通过Yield实现协程,性能较差但支持JDK1.5及以上
下面我们选择Quasar来实现协程,通过Fiber来代表一个协程。
代码
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
可以看出3个协程通过互相切换实现了协作,最终完成了执行。
Quasar是怎么实现Fiber的?
Quasar是使用Java Agent进行字节码植入的,它支持在JVM层面实现协程。
Quasar Fiber则是通过字节码修改技术在编译或载入时织入必要的上下文保存/恢复代码,通过抛异常来暂停,恢复的时候根据保存的上下文(Continuation),恢复jvm的方法调用栈和局部变量,Quasar Fiber提供相应的Java类库来实现,对应用有一定的侵入性(很小)
Quasar Fiber 主要有 Instrument + Continuation + Scheduler几个部分组成
- Instrument 做一些代码的植入,如park前后上下文的保存/恢复等;
- Continuation 保存方法调用的信息,如局部变量,引用等;
- Scheduler 调度器,负责将fiber分配到具体的操作系统线程执行
总结
协程和线程密切相关,协程可以认为是运行在线程上的代码块,协程提供的挂起操作会使协程暂停执行,而不会导致线程阻塞。
协程又是一种轻量级资源,即使创建了上千个协程,对于系统来说也不是很大的负担,但如果在程序中创建上千个线程,那系统可真就压力山大了。可以说,协程的设计方式极大地提高了线程的使用率。
在实际的案例中,协程还是在 Go 语言中的应用较为成熟,在 Java 中的协程目前还不是很稳定,重点是缺乏大型项目的验证,可以说 Java 的协程设计还有很长的路要走。
好了,以上就是今天分享的全部内容,enjoy 欢迎吐槽、交流~~ 微信公众号【AI黑板报】