
课程咨询: 400-996-5531 / 投诉建议: 400-111-8989
认真做教育 专心促就业
在软件开发行业中存在着许多的术语,比如说线程、线程池等等。今天,我们就一起来了解一下,在java编程软件开发项目中都有哪些开发术语是需要我们了解的。
什么是线程?
术语“线程”可以用来描述很多不同的事情。在本文中,我会使用它来代指一个逻辑线程。也就是:按照线性顺序的一系列操作;一个执行的逻辑路径。CPU的每个核心只能真正并发同时执行一个逻辑线程。这就带来一个固有的问题:如果线程的数量多于内核的数量,那么有的线程必须要暂停以便于其他的线程来运行工作,当再次轮到自己的执行的时候,会将任务恢复。为了支持暂停和恢复,线程至少需要如下两件事情:
某种类型的指令指针。也就是,当我暂停的时候,我正在执行哪行代码?
一个栈。也就是,我当前的状态是什么?栈中包含了本地变量以及指向变量所分配的堆的指针。同一个进程中的所有线程共享相同的堆。
鉴于以上两点,系统在将线程调度到CPU上时就有了足够的信息,能够暂停某个线程、允许其他的线程运行,随后再次恢复原来的线程。这种操作通常对线程来说是完全透明的。从线程的角度来说,它是连续运行的。线程能够感知到重新调度的方式是测量连续操作之间的计时。
JVM使用操作系统线程
尽管并非规范所要求,但是据我所知所有的现代、通用JVM都将线程委托给了平台的操作系统线程来处理。在接下来的内容中,我将会使用“用户空间线程(user space thread)”来代指由语言进行调度的线程,而不是内核/OS所调度的线程。操作系统实现的线程有两个属性,这两个属性极大地限制了它们可以存在的数量;任何将语言线程和操作系统线程进行1:1映射的解决方案都无法支持大规模的并发。
在JVM中,固定大小的栈
使用操作系统线程将会导致每个线程都有固定的、较大的内存成本
采用操作系统线程的另一个主要问题是每个OS线程都有大小固定的栈。尽管这个大小是可以配置的,但是在64位的环境中,JVM会为每个线程分配1M的栈。你可以将默认的栈空间设置地更小一些,但是你需要权衡内存的使用,因为这会增加栈溢出的风险。代码中的递归越多,就越有可能出现栈溢出。如果你保持默认值的话,那么1000个线程就将使用1GB的RAM。虽然现在RAM便宜了很多,但是几乎没有人会为了运行上百万个线程而准备TB级别的RAM。
Go的行为有何不同:动态大小的栈
Golang采取了一种很聪明的技巧,防止系统因为运行大量的(大多数是未使用的)栈而耗尽内存:Go的栈是动态分配大小的,随着存储数据的数量而增长和收缩。这并不是一件简单的事情,它的设计经历了多轮的迭代。我并不打算讲解内部的细节(关于这方面的知识,有很多的博客文章和其他材料进行了详细的阐述),但结论就是每个新建的Goroutine只有大约4KB的栈。每个栈只有4KB,那么在一个1GB的RAM上,我们就可以有250万个Goroutine了,相对于Java中每个线程的1MB,这是巨大的提升。
作者:Russell Cohen
译者:张卫滨
节选:infoq
【免责声明】:本内容转载于网络,转载目的在于传递信息。文章内容为作者个人意见,本平台对文中陈述、观点保持中立,不对所包含内容的准确性、可靠性与完整性提供形式地保证。请读者仅作参考。