橘子学java之java中的协程

一、关于协程

最近jdk19上了,java开始支持虚拟线程了,也就是所谓的协程,java的协程库是官方是这个https://openjdk.org/projects/loom/.我指的是oracle的java,阿里那个well的早就支持了,。只是官方的还不支持。
我倒是还没看那个,但是突然想起以前研究的一个东西,是一个大神自己写的协程库,也就是quasar,github的地址是这个:https://github.com/puniverse/quasar
橘子学java之java中的协程_第1张图片
下面先说一下我对协程的浅薄的理解,未必正确,不断学习。
以前我们常说的两个概念是进程和线程,也都知道进程是操作系统分配资源的单位,线程是运行程序的基本单位,一个进程里面可能有多个线程,同一个进程内部的线程共享进程资源,只是自己独自占据一些必要的运行时数据资源,然后我们还知道进程切换因为涉及资源的分配是很耗时的,线程切换比进程好一些,但是也涉及上下文的切换也是很耗的,因为他们都是属于操作系统的资源,程序运行在用户态,切换需要在内核态进行切换,所以需要程序进入内核态,这是一个很大的开销。
但是协程不是,协程是属于线程的,一个线程内部有多个协程,协程和操作系统没必然从属关系,他是多个函数,是属于用户态的,所以他的操作切换之类的不需要陷入内核,这就减少了很多开销。
他的切换只是在线程内部的切换,因为都是在用户态,所以多个协程函数只能在一个线程内部串行,因为不是属于内核的,所以不能并行调度,一个线程内部只能串行,当一个协程执行的时候,其余协程是挂起的。
OK,至此为止我们大概有个理解了。

二、quasar

quasar是一个大神写的三方的协程库,我们来看看。据说这个大神已经入职oracle了,并且正是java的虚拟线程的开发者。
https://github.com/puniverse/quasar
他是操作了字节码那块了,原理底层我们不去研究,这里先看看用法对比一下有啥优点。
我们设计一个需求:

一万和线程或者协程,运行200万次运算,看看耗时。

1、引入jar包

 
 <dependency>
     <groupId>co.paralleluniversegroupId>
     <artifactId>quasar-coreartifactId>
     <version>0.7.10version>
     <classifier>jdk8classifier>
 dependency>

2、一万个线程

/**
 * @Author levi
 * @Description //TODO 
 * @Date 21:50 2022-10-9
 * @Param  * @param null
 * @return 
 **/
public class JavaThread {
    // 100万个线程进行调度切换成本巨几把高
    static  int THREAD_LENTH = 10000;//time cost:129242

    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();
        Thread[] threads = new Thread[THREAD_LENTH];
        // 给数组里面初始化线程,以及放进去任务
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(()->{
                calc();
            });
        }

        // 在这里启动线程进行调度
        for (int i = 0; i < threads.length; i++) {
            threads[i] .start();
        }

        // 主线程在这里等等结果,等其他线程结束了一起结束
        for (int i = 0; i < threads.length; i++) {
            try {
                threads[i].join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("100万个线程运算200万次时间花销为:" + (System.currentTimeMillis() - startTime));
    }

    // 一个运算的累加函数
    public static void calc() {
        int result = 0;
        // 运算200万次,每次都加一下
        for (int i = 0; i < 200; i++) {
            for (int j = 0; j < 10000; j++) {
                result += i;
            }
        }
    }
}
1万个线程运算200万次时间花销为:8408

3、一万个协程

import co.paralleluniverse.fibers.Fiber;

/**
 * @Author levi
 * @Description //TODO 
 * @Date 21:51 2022-10-9
 * @Param  * @param null
 * @return 
 **/
public class JavaFiber {
    // 十万个协程,操作系统会合适的分布在几个线程中,我测试的三个,三个线程调度就轻多了
    static  int FIBER_LENTH = 10000;//time cost:1099

    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();
        // 创建十万个协程的一个数组
        Fiber<Void>[] fibers = new Fiber[FIBER_LENTH];
        // 给数组里面初始化协程,以及放进去任务
        for (int i = 0; i < fibers.length; i++) {
            fibers[i] = new Fiber(()->{
                calc();
            });
        }


        // 在这里启动线程进行调度
        for (int i = 0; i < fibers.length; i++) {
            fibers[i] .start();
        }

        // 主线程在这里等等结果,等其他协程结束了一起结束
        for (int i = 0; i < fibers.length; i++) {
            try {
                fibers[i].join();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        System.out.println("100万个协程运算200万次时间花销为:" + (System.currentTimeMillis() - startTime));
    }

    // 一个运算的累加函数
    public static void calc() {
        int result = 0;
        // 运算200万次,每次都加一下
        for (int i = 0; i < 200; i++) {
            for (int j = 0; j < 10000; j++) {
                result += i;
            }
        }
    }
}
1万个协程运算200万次时间花销为:627

我们看到协程的耗时比线程少了机会十多倍,这就是因为协程的切换是就在用户态,基本不会到内核态里面,都是在一个线程里面串行执行的方法,不涉及内核态用户态的切换。所以他的开销是很小的,这种优势在你线程协程个数越大的时候越明显,OK,后面我们学习一下IO模型,然后开始一点点的看看JDK虚拟线程的设计。

你可能感兴趣的:(JAVA,#,JDK,java,开发语言,jdk)