zoukankan      html  css  js  c++  java
  • JAVA协程 纤程 与Quasar 框架

    ava使用的是系统级线程,也就是说,每次调用new Thread(....).run(),都会在系统层面建立一个新的线程,然鹅新建线程的开销是很大的(每个线程默认情况下会占用1MB的内存空间,当然你愿意的话可以用-Xss来调小点),更不要说线程切换带来的开销了

    为了节省开销,程序员玩出了很多花样。

    最常用的是线程池(线程复用,但是完全无法处理阻塞调用的问题)

    以及事件驱动框架(NIO或者Netty,用少量的工作线程来服务大量的慢速IO连接,但是EventLoop中也不能有阻塞调用,耗时的逻辑必须放在额外的线程池里处理)

    但是NIO的代码难写也难懂,像我这种懒惰的程序猴子,最喜欢的还是一个线程对应一个连接这种简单粗暴的编程手法。

    纤程(Coroutine)是我们的救星

    所谓的纤程,或者协程,可以理解为是一种轻量级的线程,它与线程的主要区别在于

    a. 线程切换的过程是由系统内核完成,切换的过程中会进入到内核态。而纤程则完全工作在用户态。

    b. 线程是否发生切换是由操作系统决定的(抢占式调度),工作线程本身没有决定权。而纤程的切换是需要工作纤程主动放弃CPU,这样调度器才能让另外一个纤程继续运行。

    很多语言已经内置了纤程,最著名的应该就是Go了,用go关键字,就能直接创建一个纤程并在其中为所欲为,其他的Scheduler会自动帮你搞定。所以Go能相对容易的写出正确的高并发程序。

    可惜的是,Java没有官方的纤程支持,好在有个叫做Quasar的库可堪一用

    使用这个lib,你就能在Java程序中创建纤程了,代码大概长这个样子:

    复制代码
        public static void main(String[] args)
                throws ExecutionException, InterruptedException, SuspendExecution {
            int FiberNumber = 1_000_000;
            CountDownLatch latch = new CountDownLatch(1);
            AtomicInteger counter = new AtomicInteger(0);
    
            for (int i = 0; i < FiberNumber; i++) {
                new Fiber(() -> {
                    counter.incrementAndGet();
                    if (counter.get() == FiberNumber) {
                        System.out.println("done");
                    }
                    Strand.sleep(1000000);
                }).start();
            }
            latch.await();
        }
    复制代码

    在上面这段代码中,我们直接创建了一百万个纤程,如果是一般的Thread,不考虑OS能否负担得起,单单占用的内存就要1T起步。

    但是这段程序实际占用的内存只在1G出头,也就是说每个纤程的内存占用只在1K左右。

    这是如何做到的?

    Quasar在编译时会对代码进行扫描,如果方法带有Suspendable注解,或者抛出SuspendExecution,或者在配置文件中被指定,Quasar会直接修改生成的字节码,在park方法的前后,插入一些字节码。

    这些字节码会记录此时纤程的执行状态(相关的局部变量与操作数栈),然后通过抛出异常的方式将CPU的控制权从当前协程交回到控制器

    此时控制器可以再次调度另外一个纤程运行,并通过之前插入的那些字节码恢复当前纤程的执行状态,使程序能继续正常执行。

    并且,这些操作是非常轻量的,所以内存消耗极小,也不会对CPU带来太多的额外开销(据说在3%-5%)

  • 相关阅读:
    将指定json格式的内容,写入文件中,构造测试数据
    shell对比用=时 记得加空格
    scp带私钥使用以及免密配置
    kafka知识补充
    根据frm和ibd文件恢复数据库表结构和数据
    登录页面 逻辑:当用户进来的时候, 全局检查一下是否有用户的信息,如果用则显示用户信息页面;没有,则显示用户登录页面
    理解Spring 容器、BeanFactory 以及 ApplicationContext
    Java 中 CAS
    volatile 关键字
    JenKins docker 集群
  • 原文地址:https://www.cnblogs.com/cfas/p/11076022.html
Copyright © 2011-2022 走看看