精华内容
下载资源
问答
  • 传智播客博学谷加小谷vx:boxueguu免费领IT各学科资料、课程Get更多干货直播课程序员常用的API接口管理工具有哪些?通过API管理工具和平台能够大大简化API管理的难度和复杂度。API应用程序接口是一些预先定义的函数...
    a3bc3d7784a36fde8aaef4215cd32be3.png

    传智播客博学谷

    加小谷vx:boxueguu

    免费领IT各学科资料、课程

    Get更多干货直播课

    b35162fa58a10f70e8c8268234151ac9.png

        程序员常用的API接口管理工具有哪些?通过API管理工具和平台能够大大简化API管理的难度和复杂度。API应用程序接口是一些预先定义的函数,或指软件系统不同组成部分衔接的约定。API接口属于一种操作系统或程序接口。

    fae42a0f3752c1fcfb23b2d72f4c1e7b.png

        API在软件开发过程中很关键,对API管理格外重要。通过API管理工具和平台能够大大简化API管理的难度和复杂度。下面介绍程序员常用的API接口管理工具。

    1、APIUmbrella

        用于管理API和微服务的顶级开源工具之一。通过为不同的域授予不同的管理员权限,可以使多个团队使用同一个Umbrella。提供速率限制,API密钥,缓存,实时分析和Web管理界面等功能。

    2、Gravitee.io

        用于管理API的开源平台,灵活的并且是轻量级的。具有开箱即用的功能,例如速率限制,IP过滤,跨域资源共享,即插即用选项,具有基于OAuth2和JSONWeb令牌策略的开发者门户,负载平衡等。但此API管理工具的主要功能是能够生成细粒度的报告以理解API的数据是如何使用。

    3、APIman.io

        由RedHat引入的一个顶级API管理平台,在GitHub中可以找到,为后端开发人员提供了很多便利,包括快速运行具有可分离策略引擎的基于策略的治理异步功能增强的结算和分析选项RESTAPI可用性的管理限速。

    4、WSO2API管理器

        完整的生命周期API管理平台,可以随时随地运行。在企业内部和私有云上执行API的分发和部署。除此之外,还提供高度定制化管理策略易用,为SOAP或RESTfulAPI设计和原型的可能性,更好的访问控制和货币化设施等。

    5、KongEnterprise

        广泛采用的开源微服务API工具,使开发人员能够快速,轻松,安全地管理一切。的企业版带有许多特性和功能,例如:开源插件的可用性一键式操作通用语言基础架构功能强大的可视化监控功能常规软件运行状况检查OAuth2.0权限,以及更广泛的社区支持。

    6、Tyk.io

        用Go编程语言编写,也是公认的开源API网关。带有开发者门户,详细的文档,用于API分析的仪表板,API的速率限制,身份验证以及各种其他此类规范,可帮助组织专注于微服务环境和容器化。但基于商业的服务仅适用于付费版本。

    7、Fusio

        另一个开源API管理工具,开发人员可以使用从不同的数据类型创建和维护RESTAPI。具有高效的生命周期管理功能,例如用于管理控制的后端仪表板,详细的文档,用于传入请求的JSON验证以及满足用户权限的范围处理。此APIM平台会自动生成OAI和RAML要求,并根据定义的架构创建自定义的客户端SDK。

    8、Apigility

        由Zend框架设计和维护,是考虑用于API管理的下一个开源框架。该平台创建并展示其代码的JSON表示形式。还为他们提供了不同的版本控制选项,以及通过OAuth2进行身份验证的简便性和包含API蓝图的文档。API接口管理,这15种开源工具助你管理APIApigility。

    9、SwaggerHub

        被40多个组织考虑用于管理API,也是最好的开源API管理工具之一。该平台为后端开发领域的设计人员和开发人员提供了广泛的选择。为他们提供了强大而直观的编辑器,可在保持设计一致性的同时提供更高的效率和速度。此外,还提供了智能错误反馈,语法自动完成和多种样式验证器可用性的机会。

    10、APIAxle

        在Exicon的支持下,APIAxle是另一种开源,简单且轻量级的代理,为开发人员提供了很多好处,例如:实时分析强大的身份验证,记录API流量以进行统计和报告,易于创建和管理API密钥,以及支持RESTAPI设计以及Go,PHP和Node.js库的使用。

        API在软件、Web和移动应用程序开发领域应用,从企业内部到面向公众的应用以及与合作伙伴进行系统集成。使用API,开发人员可以创建满足各种客户需求的应用程序。总结程序员常用的API接口管理工具:APIUmbrella、Gravitee.io、APIman.io、WSO2API管理器、KongEnterprise、Tyk.io、Fusio、Apigility、SwaggerHub、APIAxle,希望对大家学习有所帮助。

    -End-

    216a3be89a2e4cf9d8046e789b688605.gif

    ce6685b07b03f90134a510d16773df54.png▼点击【阅读原文】免费领课!我就知道你 “在看”
    展开全文
  • 1.什么是API? 可以理解为Java自己提供的标准类库,开发人员可直接使用其方法, 而不用进行源码实现。...了解API是什么之后,再看一下Java中常用的API有哪些: System类 StringBuffer&StringBuilder类 Mat
  • 文章目录知识储备native方法线程创建API底层实现JVM中的start0方法JavaThread对象...本章节开始进行发散,来看看常用的Thread操作相关的API有哪些。 本部分所有内容,JDK和JVM信息如下: java version “15.0.1” 202

    知识储备

    上一章节中我们讨论了Thread的状态转换,以及Thread状态和OS状态之间的映射关系。本章节开始进行发散,来看看常用的Thread操作相关的API有哪些。

    本部分所有内容,JDK和JVM信息如下:

    java version “15.0.1” 2020-10-20
    Java™ SE Runtime Environment (build 15.0.1+9-18)
    Java HotSpot™ 64-Bit Server VM (build 15.0.1+9-18, mixed mode, sharing)

    Oracle JDK 15从官网可以直接下载,HotSpot源码参见如下:

    1. HotSpot的开源项目位于:OpenJDK/JDK
    2. 源代码仓库为:OpenJdk
    3. 仓库中源代码对应JDK 15的TAG为jdk-15-ga
    4. 我的所有中文注释均上传在我个人fork出来的仓库中,参见YunyaoLong/jdk,提交分支为jdk-15-ga-ch-cn

    太长不看

    线程的创建其实是JVM将操作系统的线程创建做了一次二次封装。

    整体主要的线程创建的流程为:

    1. 计算线程所需要的内存空间;
    2. 向操作系统申请创建一个线程;
    3. 对该线程设置优先级;
    4. 将线程加入线程队列;
    5. 将线程的状态设置成RUNNABLE并且让操作系统尝试唤醒。

    这由于不同的操作系统对于线程的优先级的设置的范围不一样,因此在代码开发过程中,不建议针对线程设置不同的优先级

    如果一定需要设置优先级,也建议尽可能地减少优先级的梯度,同时让这些优先级尽可能均匀的分散在java.lang.Thread#MIN_PRIORITYjava.lang.Thread#MAX_PRIORITY之间(通常对应的是1-10)。

    或者翻阅源码或者相关操作系统手册,确定自己优先级设置能够正常映射到对应的优先级上,否则就可能出现无法预料的线程调度错乱问题。

    native方法

    在看线程创建之前,我们首先要知道什么是native方法。

    我们都知道,在Java中我们的所有代码都是在JVM中运行的,JVM通过一次虚拟化,对上层代码屏蔽了下游OS和硬件的差异。但是我们在实际项目中又不可避免的需要对OS或者硬件做操作,比如操作文件,操作线程,获取键盘/摄像头输入等。

    这时候就需要我们的JNI和native方法相互配合了。

    Java定义了JNI(Java Native Interface, Java本地接口)用于Java程序片段和本地其他类型的语言进行交互和调用,所有的JNI定义完成后,均使用native关键字进行修饰。JVM则封装了对应的native方法用于接口的具体调用。

    本次Thread相关的native方法均为C++编写,因此如果之前完全没有学习过C++的同学,先稍微学习一下C++的基础知识再继续往下看。

    线程创建

    API

    我们在上一章节中,知道了Thread的激活方法是java.lang.Thread#start

    该方法调用完成之后,JVM就会帮我们创建一个线程,并将其状态标记为NEW状态,并且放进操作系统的就绪队列。

    当该线程被从操作系统拉出来执行后,状态将变成RUN,并开始真正进行工作。

    底层实现

    JVM中的start0方法

    通过跟踪源代码,我们可以发现线程的创建实际上是通过一个java.lang.Thread#start0native方法具体实现的。

    (这里其实可以发现start0仅会被start方法调用,也说明了线程的创建方式有且只有java.lang.Thread#start

    通过代码追踪,我们首先可以找到start0的入口函数位置,位于src\java.base\share\native\libjava中的Thread.c库,在这个库中,HotSpo定义了一组关系:

    static JNINativeMethod methods[] = {
        {"start0",           "()V",        (void *)&JVM_StartThread},
        {"stop0",            "(" OBJ ")V", (void *)&JVM_StopThread},
        {"isAlive",          "()Z",        (void *)&JVM_IsThreadAlive},
        {"suspend0",         "()V",        (void *)&JVM_SuspendThread},
        {"resume0",          "()V",        (void *)&JVM_ResumeThread},
        {"setPriority0",     "(I)V",       (void *)&JVM_SetThreadPriority},
        {"yield",            "()V",        (void *)&JVM_Yield},
        {"sleep",            "(J)V",       (void *)&JVM_Sleep},
        {"currentThread",    "()" THD,     (void *)&JVM_CurrentThread},
        {"interrupt0",       "()V",        (void *)&JVM_Interrupt},
        {"holdsLock",        "(" OBJ ")Z", (void *)&JVM_HoldsLock},
        {"getThreads",        "()[" THD,   (void *)&JVM_GetAllThreads},
        {"dumpThreads",      "([" THD ")[[" STE, (void *)&JVM_DumpThreads},
        {"setNativeName",    "(" STR ")V", (void *)&JVM_SetNativeThreadName},
    };
    

    因此我们的start0就对应了其中的JVM_StartThread方法。

    因此我们继续查阅到src/hotspot/share/include/jvm.h库头,发现了其中的JVM_StartThread方法。对应的,我们找到其实现类src\hotspot\share\prims\jvm.cpp

    JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
      JVMWrapper("JVM_StartThread");
      JavaThread *native_thread = NULL;
      // 线程操作的锁获取标记
      bool throw_illegal_thread_state = false;
    
      // 线程对象的空间申请代码块
      {
        // 尝试获取锁
        MutexLocker mu(Threads_lock);
    
        // 保证线程由于Thread.state为后期(Java 1.5)引入,因此需要确定state非null,防止查询Thread.state时出现空指针异常
        if (java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) != NULL) {
          throw_illegal_thread_state = true;
        } else {
          // 计算线程的栈空间
          jlong size = java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread));
          // 为native_thread申请内存空间(申请32位还是64位视操作系统决定,所以是使用size_t防止爆int)
          NOT_LP64(if (size > SIZE_MAX) size = SIZE_MAX;)
          size_t sz = size > 0 ? (size_t) size : 0;
          native_thread = new JavaThread(&thread_entry, sz);
    
          // 防止操作系统因为内存不足而导致native_thread创建失败
          if (native_thread->osthread() != NULL) {
            // 标记当前线程的状态为prepare
            native_thread->prepare(jthread);
          }
        }
      }
    
      // 检查JNI传入的线程对象的状态,如果为空,就抛出异常
      if (throw_illegal_thread_state) {
        THROW(vmSymbols::java_lang_IllegalThreadStateException());
      }
    
      // 断言native_thread已经成功分配到对象空间
      assert(native_thread != NULL, "Starting null thread?");
    
      if (native_thread->osthread() == NULL) {
        // 如果native_thread对象空间中,没有实际申请到OS线程,进行异常处理并且抛出异常
        native_thread->smr_delete();
        if (JvmtiExport::should_post_resource_exhausted()) {
          JvmtiExport::post_resource_exhausted(
            JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_THREADS,
            os::native_thread_creation_failed_msg());
        }
        THROW_MSG(vmSymbols::java_lang_OutOfMemoryError(),
                  os::native_thread_creation_failed_msg());
      }
    
    // 如果有加载Java Flight Recorder工具,针对JFR做特殊处理
    #if INCLUDE_JFR
      if (Jfr::is_recording() && EventThreadStart::is_enabled() &&
          EventThreadStart::is_stacktrace_enabled()) {
        JfrThreadLocal* tl = native_thread->jfr_thread_local();
        // skip Thread.start() and Thread.start0()
        tl->set_cached_stack_trace_id(JfrStackTraceRepository::record(thread, 2));
      }
    #endif
    
      // 调用Thread::start方法开始进行线程操作
      Thread::start(native_thread);
    
    JVM_END
    

    其中有几个比较重要的函数:

    1. 线程对象创建函数new JavaThread(&thread_entry, sz)
    2. OS线程查看入口native_thread->osthread()
    3. 线程状态初始化函数native_thread->prepare(jthread)
    4. 线程启动函数Thread::start(native_thread)

    为了深入理解这些函数,我们一个一个展开这些代码的具体实现。

    JavaThread对象及构造函数

    JavaThread的结构定义位于src/hotspot/share/runtime/thread.hpp

    其中定义的参数比较多,不建议挨个查看作用,整体和Thread的关系可以参考官方注释中的结构:

    // - Thread
    //   - JavaThread
    //     - various subclasses eg CompilerThread, ServiceThread
    //   - NonJavaThread
    //     - NamedThread
    //       - VMThread
    //       - ConcurrentGCThread
    //       - WorkerThread
    //         - GangWorker
    //     - WatcherThread
    //     - JfrThreadSampler
    

    JavaThread构造函数的具体实现参见src/hotspot/share/runtime/thread.cpp

    JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) :
                           Thread() {
      initialize(); // JavaThread对象的参数初始化,不建议细看
      _jni_attach_state = _not_attaching_via_jni;
      set_entry_point(entry_point);
      // 定义需要创建的线程为java_thread
      os::ThreadType thr_type = os::java_thread;
      thr_type = entry_point == &compiler_thread_entry ? os::compiler_thread :
                                                         os::java_thread;
      // 调用线程创建接口
      os::create_thread(this, thr_type, stack_sz);
      // 以下内容主要自官方注释:
      // 当操作系统没有可分配的内存的时候,_osthread有可能返回NULL。
      // 但是此时我们又不能抛出OOM Error,因为调用方此时可能持有了一把锁(参见上方的mu(Threads_lock)),这些锁必须在抛出异常前释放掉。如果我们需要抛出的异常,就需要为这个Exception对象申请内存,并初始化异常对象,此时可能已经没有足够内存了,这种行为就有可能锁回收失败。
      // 因此建议JVM先释放所有的锁,然后Java代码中如果发现线程申请失败的话,自己抛出异常(也可以触发OOM Error)。(译者注: 然而好像目前主要的Java发行版,都没有针对这种场景抛出异常)
      //
      // 代码运行到这里的时候,线程依然保持挂起的状态。该线程必须由创建者(JVM)执行运行(start)动作。
      // 此外,线程的创建者(JVM)需要调用Threads:add用于将创建之后的线程放入线程队列中,本处代码没有添加是因为每一个线程对象必须完全创建(而此处可能会创建失败)
    }
    

    这里又涉及到了一个接口——os::create_thread(this, thr_type, stack_sz)。我们继续深挖它。

    os:create_thread方法

    该方法的接口定义在src\hotspot\share\runtime\os.hpp,在不同的操作系统中,有不同的实现,参见os_linux.cppos_windows.cppos_posix.cppos_aix.cppos_bsd.cpp

    这里以linux系统中的实现os_linux.cpp为例,linux中的线程创建,使用的是标准的pthread库。

    (windows使用的是MSDN_beginthreadex方法,具体实现可以自行跟踪源码,本次不做展开)

    bool os::create_thread(Thread* thread, ThreadType thr_type,
                           size_t req_stack_size) {
      assert(thread->osthread() == NULL, "caller responsible");
    
      // 创建一个OSThread对象
      OSThread* osthread = new OSThread(NULL, NULL); // 调用pd_initialize()接口并初始化两个入参
      if (osthread == NULL) {
        return false;
      }
    
      // 设置线程类型(就是上方传入的java_thread)
      osthread->set_thread_type(thr_type);
    
      // 设置当前线程状态为已分配(ALLOCATED),但是还未初始化(INITIALIZED)
      osthread->set_state(ALLOCATED);
    
      thread->set_osthread(osthread);
    
      // 调用pthread库初始化线程属性
      pthread_attr_t attr;
      pthread_attr_init(&attr);
      pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    
      // 申请req_stack_size大小的线程。
      size_t stack_size = os::Posix::get_initial_stack_size(thr_type, req_stack_size);
      // 在2.7和之前的glibc版本中,guard size的计算机制没有正确实现。
      // posix标准要求线程的栈空间中包含将guard pages的诉求,而Linux创建线程的时候,线程栈大小(stacksize)并没有包含guard pages的诉求。 
      // 因此,JVM通过手工计算保护页面的大小来调整请求的stack_size,以模仿适当的行为。
      // 但是,请注意最终计算出来的栈大小不要以0结尾,否则可能会出现栈溢出(overflow)。 在这种情况下,请勿不要添加guard pages的诉求。
      size_t guard_size = os::Linux::default_guard_size(thr_type);
      // 设置glibc库中的guard page属性。
      // 由于get_static_tls_area_size()需要读取guard page属性,因此该方法必须在get_static_tls_area_size()之前调用。
      pthread_attr_setguardsize(&attr, guard_size);
    
      size_t stack_adjust_size = 0;
      if (AdjustStackSizeForTLS) {
        // 为线程本地存储(Thread Local Storage, TLS)预留资源,参见get_static_tls_area_size().
        stack_adjust_size += get_static_tls_area_size(&attr);
      } else {
        stack_adjust_size += guard_size;
      }
    
      // 根据操作系统的分页大小,让栈的诉求向上对齐
      stack_adjust_size = align_up(stack_adjust_size, os::vm_page_size());
      if (stack_size <= SIZE_MAX - stack_adjust_size) {
        stack_size += stack_adjust_size;
      }
      assert(is_aligned(stack_size, os::vm_page_size()), "stack_size not aligned");
    
      int status = pthread_attr_setstacksize(&attr, stack_size);
      assert_status(status == 0, status, "pthread_attr_setstacksize");
    
      ThreadState state;
    
      {
        pthread_t tid;
        // 调用pthread_create创建线程,同时获取线程创建结果(0成功,其余失败)
        int ret = pthread_create(&tid, &attr, (void* (*)(void*)) thread_native_entry, thread);
    
        char buf[64];
        if (ret == 0) {
          log_info(os, thread)("Thread started (pthread id: " UINTX_FORMAT ", attributes: %s). ",
            (uintx) tid, os::Posix::describe_pthread_attr(buf, sizeof(buf), &attr));
        } else {
          log_warning(os, thread)("Failed to start thread - pthread_create failed (%s) for attributes: %s.",
            os::errno_name(ret), os::Posix::describe_pthread_attr(buf, sizeof(buf), &attr));
          // Log some OS information which might explain why creating the thread failed.
          log_info(os, thread)("Number of threads approx. running in the VM: %d", Threads::number_of_threads());
          LogStream st(Log(os, thread)::info());
          os::Posix::print_rlimit_info(&st);
          os::print_memory_info(&st);
          os::Linux::print_proc_sys_info(&st);
          os::Linux::print_container_info(&st);
        }
    
        pthread_attr_destroy(&attr);
    
        if (ret != 0) {
          // 如果创建失败,则释放osthread申请的内存空间
          thread->set_osthread(NULL);
          delete osthread;
          return false;
        }
    
        // 将创建完成的操作系统线程id缓存到osthread对象中
        osthread->set_pthread_id(tid);
    
        // 如果自己的子线程还在初始化完成(处于ALLOCATED但是没有到INITIALIZED)
        {
          Monitor* sync_with_child = osthread->startThread_lock();
          MutexLocker ml(sync_with_child, Mutex::_no_safepoint_check_flag);
          while ((state = osthread->get_state()) == ALLOCATED) {
            sync_with_child->wait_without_safepoint_check();
          }
        }
      }
    
      // 如果当前线程已经是一个僵尸进程(ZOMBIE),则手工释放osthread申请的内存空间
      if (state == ZOMBIE) {
        thread->set_osthread(NULL);
        delete osthread;
        return false;
      }
    
      // 线程创建完成,状态为INITIALIZED
      assert(state == INITIALIZED, "race condition");
      return true;
    }
    

    到这里操作系统线程已经通过pthread库创建完成了。我们start0方法也终于执行到了native_thread = new JavaThread(&thread_entry, sz);这一行。

    osthread方法

    这个暂未找到在哪实现,但是其实我们看上面osthread的创建代码,其实就很容易何以知道这个方法就是返回create_thread中创建好的osthread

    JavaThread::prepare方法

    再次回到我们的JavaThread实现类中来,我们看看prepare代码的具体实现。同样还是以Linux为例(注,在上文我们知道Linux使用的是pthread库进行的创建,也即posix标准线程)。

    void JavaThread::prepare(jobject jni_thread, ThreadPriority prio) {
    
      assert(Threads_lock->owner() == Thread::current(), "must have threads lock");
      assert(NoPriority <= prio && prio <= MaxPriority, "sanity check");
      // 使用Handle来绑定Java的Thread对象和C++的Thread
    
      // 使用JNIHandles从jni线程中获取C++的线程对象,并将其放入一个新的Handle封装类的变量thread_oop中。
      // 这个Handle主要用于共享该C++线程对象。
    
      Handle thread_oop(Thread::current(),
                        JNIHandles::resolve_non_null(jni_thread));
      assert(InstanceKlass::cast(thread_oop->klass())->is_linked(),
             "must be initialized");
      set_threadObj(thread_oop());
      java_lang_Thread::set_thread(thread_oop(), this);
    
      if (prio == NoPriority) {
        prio = java_lang_Thread::priority(thread_oop());
        assert(prio != NoPriority, "A valid priority should be present");
      }
    
      // 将线程的优先级设置成 Java中优先级 映射之后的 实际操作系统的线程优先级
      Thread::set_priority(this, prio);
    
      // 将线程加入线程队列中,并且该线程进行激活。
      // 调用Threads::add时需要持有该线程的锁。
      // 至关重要的一点是,在我们线程加入线程队列动作执行挖成之后,我们需要释放该线程的锁
      // (也即JVM不会主动阻塞该线程),以防止GC无法回收该线程的内存。
      Threads::add(this);
    }
    

    这其中调用了thread.cpp中的方法Thread::set_priority对优先级进行设置。

    void Thread::set_priority(Thread* thread, ThreadPriority priority) {
      debug_only(check_for_dangling_thread_pointer(thread);)
      // Can return an error!
      (void)os::set_priority(thread, priority);
    }
    

    Thread::set_priority调用了os.cpp中os::set_priority进行实际的优先级测试。代码位于src\hotspot\share\runtime\os.cpp

    OSReturn os::set_priority(Thread* thread, ThreadPriority p) {
      debug_only(Thread::check_for_dangling_thread_pointer(thread);)
    
      if ((p >= MinPriority && p <= MaxPriority) ||
          (p == CriticalPriority && thread->is_ConcurrentGC_thread())) {
        int priority = java_to_os_priority[p];
        return set_native_priority(thread, priority);
      } else {
        assert(false, "Should not happen");
        return OS_ERR;
      }
    }
    

    方法设置中,有一个关键的优先级获取列表——java_to_os_priority。这个列表在不同的操作系统中,针对各个操作系统对于线程优先级的不同定义,进行了不同的实现。

    // linux中优先级的实现,参见src\hotspot\os\linux\os_linux.cpp
    int os::java_to_os_priority[CriticalPriority + 1] = {
      19,              // 0 Entry should never be used
    
       4,              // 1 MinPriority
       3,              // 2
       2,              // 3
    
       1,              // 4
       0,              // 5 NormPriority
      -1,              // 6
    
      -2,              // 7
      -3,              // 8
      -4,              // 9 NearMaxPriority
    
      -5,              // 10 MaxPriority
    
      -5               // 11 CriticalPriority
    };
    
    // Windows中的实现,参见src\hotspot\os\windows\os_windows.cpp
    int os::java_to_os_priority[CriticalPriority + 1] = {
      THREAD_PRIORITY_IDLE,                         // 0  Entry should never be used
      THREAD_PRIORITY_LOWEST,                       // 1  MinPriority
      THREAD_PRIORITY_LOWEST,                       // 2
      THREAD_PRIORITY_BELOW_NORMAL,                 // 3
      THREAD_PRIORITY_BELOW_NORMAL,                 // 4
      THREAD_PRIORITY_NORMAL,                       // 5  NormPriority
      THREAD_PRIORITY_NORMAL,                       // 6
      THREAD_PRIORITY_ABOVE_NORMAL,                 // 7
      THREAD_PRIORITY_ABOVE_NORMAL,                 // 8
      THREAD_PRIORITY_HIGHEST,                      // 9  NearMaxPriority
      THREAD_PRIORITY_HIGHEST,                      // 10 MaxPriority
      THREAD_PRIORITY_HIGHEST                       // 11 CriticalPriority
    };
    

    Thread::start方法

    该方法的具体实现参见src/hotspot/share/runtime/thread.cpp

    void Thread::start(Thread* thread) {
      // start和resume的区别在于,start方法的安全性是上下文或者Java代码中的Thread方法调用的
      if (!DisableStartThread) {
        if (thread->is_Java_thread()) {
          // 初始化线程状态为RUNNABLE。如果先调用start_thread启动线程,那么线程的状态将由于无法预知而不能准确设置上。(有可能没有被成功获取到线程锁(MONITOR_WAIT)或者休眠(SLEEPING)或者其他状态)
          java_lang_Thread::set_thread_status(((JavaThread*)thread)->threadObj(),
                                              java_lang_Thread::RUNNABLE);
        }
        os::start_thread(thread);
      }
    }
    
    void os::start_thread(Thread* thread) {
      MutexLocker ml(thread->SR_lock(), Mutex::_no_safepoint_check_flag);
      OSThread* osthread = thread->osthread();
      osthread->set_state(RUNNABLE);
      // 将操作系统的线程的状态设置成RUNNABLE
      pd_start_thread(thread);
    }
    

    同样以os_linux实现为例。

    void os::pd_start_thread(Thread* thread) {
      OSThread * osthread = thread->osthread();
      assert(osthread->get_state() != INITIALIZED, "just checking");
      Monitor* sync_with_child = osthread->startThread_lock();
      MutexLocker ml(sync_with_child, Mutex::_no_safepoint_check_flag);
      // 尝试唤起该osthread
      sync_with_child->notify();
    }
    

    总结

    写到这里,最后总结一下,通过源码阅读,我们其实已经不难发现在HotSpot虚机中,线程的创建其实是JVM将操作系统的线程创建做了一次二次封装。

    整体主要的线程创建的流程为:

    1. 计算线程所需要的内存空间;
    2. 向操作系统申请创建一个线程;
    3. 对该线程设置优先级;
    4. 将线程加入线程队列;
    5. 将线程的状态设置成RUNNABLE并且让操作系统尝试唤醒。

    这里面有一个比较重要的注意事项,由于不同的操作系统对于线程的优先级的设置的范围不一样(参见上方os::java_to_os_priority)。

    因此在代码开发过程中,不建议针对线程设置不同的优先级

    如果一定需要设置优先级,也建议尽可能地减少优先级的梯度,同时让这些优先级尽可能均匀的分散在java.lang.Thread#MIN_PRIORITYjava.lang.Thread#MAX_PRIORITY之间(通常对应的是1-10)。

    或者翻阅源码或者相关操作系统手册,确定自己优先级设置能够正常映射到对应的优先级上,否则就可能出现无法预料的线程调度错乱问题。

    展开全文
  • 1.回调函数的参数有哪些,返回值如何处理。 2.不修改原来数组。 二、用ES5实现数组reduce方法 核心要点: 1、初始值不传怎么处理 2、回调函数的参数有哪些,返回值如何处理。 三、实现call/apply 思路: 利用...

    一、用ES5实现数组的map方法
    核心要点:

    1.回调函数的参数有哪些,返回值如何处理。

    2.不修改原来的数组。
    在这里插入图片描述
    二、用ES5实现数组的reduce方法
    核心要点:

    1、初始值不传怎么处理

    2、回调函数的参数有哪些,返回值如何处理。
    在这里插入图片描述
    三、实现call/apply
    思路: 利用this的上下文特性。
    在这里插入图片描述
    实现Object.create方法(常用)
    在这里插入图片描述

    展开全文
  • 公司新人培训过程中,...6.集合的使用、日期API的使用(不单是指Date 、Calender,需要会用新的API)、 7.字符串String的常用方法及灵活使用、 8.再一个就是“比较”吧!引用类型对象的比较、排序之类的方式方法。 9

    公司新人培训过程中,我们还是偏重于
    1.基础语法的夯实、方法的有效定义、方法重载及重写的规范、
    2.多态的理解和应用、不同接口的实现(函数接口对应的Lambda表达式应用)、
    3.注解的使用、自定义、
    4.泛型的理解、定义、及使用、
    5.静态static的理解与使用;这些都是基础的东西;后面就是:
    6.集合的使用、日期API的使用(不单是指Date 、Calender,需要会用新的API)、
    7.字符串String的常用方法及灵活使用、
    8.再一个就是“比较”吧!引用类型对象的比较、排序之类的方式方法。
    9.I/O、NIO也是需要的;
    10.对于多线程、通信之类的,用的场景不多。
    届时需要的时候补一补也行。线程比较复杂些。通信这块,在多线程环境下通信,也比较复杂。可以先放一放、遇到相关技术的项目了,再补,来得及也是。呵呵!
    咕嘟咖啡杨老师的技术课:第一篇-Java编程语言基础

    咕嘟咖啡杨老师的技术课:第二篇-Java面向对象编程

    咕嘟咖啡杨老师的技术课:第三篇-Java编程语言高级特性

    展开全文
  • sklearn API快速上手

    2019-12-19 17:29:10
    本文我们将依据传统机器学习的流程,看看在每一步流程中都有哪些常用的函数以及它们的用法是怎么样的。希望你看完这篇文章可以最为快速的开始你的学习任务。 1. 获取数据 1.1 导入sklearn数据集 sklearn中包含了大量...
  • 如何Hook Windows API

    2010-07-31 17:04:00
    相信大家一定经常碰到内存泄漏的问题,在诊断过程中常用的一个工具就是LeakDiag,该工具可以让用户制定要查看哪些堆中的内存分配情况。碰巧我在项目中也需要类似的功能,但是由于当时不知道LeakDiag这个工具...
  • 【JavaScript】第七章 JS-Web-API DOM&BOM

    千次阅读 2019-03-16 14:45:50
    DOM操作常用API有哪些? DOM节点attr和property有何区别? 如何检测浏览器类型? 拆解URL各个部分 回顾JS基础知识 特点:表面看来并不能用于工作中开发代码 内置函数:Object、Array、Boolean、String等 ...
  • DOM操作常用API有哪些? DOM节点attr和property有何区别? 如何检测浏览器类型? 拆解URL各个部分 回顾JS基础知识 特点:表面看来并不能用于工作中开发代码 内置函数:Object、Array、Boolean、String等 ...

空空如也

空空如也

1 2 3 4 5 ... 9
收藏数 171
精华内容 68
关键字:

常用的api函数有哪些