在我们开始谈线程之前,不得不提下进程。
无论进程还是线程都是很抽象的概念,有一个关于进程和线程很形象的比喻能帮我们更好的理解。

进程就像个房子,房子是一个包含了特定属性的容器,例如空间大小、卧室数量等。 如果你也这样看的话,那么房子自己不会主动做任何事情,它是被动的对象。 而线程则像是房中的居住者,它是主动的对象——居住者要使用不同的房间、看电视、煮饭、洗澡等等。 房子占据着一块真实的土地,正像进程占据着内存。 而房子的居住者可以自由出入所有的房间,而进程中的线程也是类似的,可以自由访问任何进程占据的内存。

按照教科书上的定义,进程是资源管理的最小单位,线程是程序执行的最小单位。 通过上面的比喻,我们可以更容易的理解进程和线程的关系。 进程只是一个容器对象,它负责占据资源(内存地址、文件I/O),而线程共享进程的资源,作为CPU调度的基本单位可以被独立调度。

线程实现

回到我们的题目:java 线程。 java 作为一个跨平台的语言,自然要提供一个跨平台的线程实现。 线程按类型可以分为内核线程(Kernel-Level Thread)和用户线程(User Thread),分类的标准主要是线程的调度者在核内还是在核外。 早期时,一些操作系统因为没有提供线程的原生实现,所以早在JDK1.2之前,java是基于用户线程来实现的。 用户线程是相对内核线程而言,内核线程自然是由操作系统内核支持的线程,由内核来管理和调度。 后来主流操作系统都支持了线程,因此现在java都采用原生线程来实现了。

既然现在的java线程都采用原生系统线程来实现,那么是否每个java线程就对应一个系统内核线程? 对sun jdk而言,在Windows和Linux中都是采用的一对一模型,Linux提供一种称为轻量级进程(LWP)的高级抽象来避免应用直接使用内核线程。 而在像Solaris这样的系统中则不一定了,因为它支持多对多模型。 不过对于底层系统的线程模型到底如何,对java线程而言都是被屏蔽了的,jvm层面提供了一个统一的抽象线程模型。 下图展示了在Linux上java线程实现的模型图

线程数量

曾经碰到一个问题,java程序运行中抛出一个OOM错误如下:
java.lang.OutOfMemoryError: unable to create new native thread
这个问题的原因可能有两种,一种是内存真的不足了,自然无法再创建线程。 另外一种其实是来自操作系统的限制,比如在Linux中,java线程会映射为轻量级进程,那么创建线程的数量自然会受到系统进程数量等资源约束的限制。

对于一个java进程到底能创建多少线程呢,一般我们按经验线程都是在几十到几百之间,顶多1、2k了。 这是为什么呢?java有个启动参数-Xss1m表明每个线程栈大小为1m,那么对内存一般2G的话,总线程数达到2k感觉上都是不可能的。 但实际上做个实验在循环中不断创建新线程,可以不断创建多达几万的线程,这又是为什么? 原因是新创建的线程其实仅仅分配了内存地址空间,但并没有实际去占用那1m的栈空间,栈空间是在线程使用时才去实际占用的。 所以经验是对的,一般对2G的堆内存空间线程数量根据应用类型在几十到几百之间是合适的。

线程状态

java定义了6种线程状态,任一时刻一个线程处于其中一种状态,其状态转换关系如下图:


1. NEW
新创建未启动的线程处于该状态
2. RUNNABLE
调用了start()方法后,线程进入RUNNABLE状态
3. WAITING
不设置timeout的Object.wati()、Thread.join()等方法会让线程进入无限等待,需要等待其他线程显式的唤醒。
4. TIMED_WAITING
Thead.sleep()或设置了timeout的Object.wati()、Thread.join()等方法让线程进入限期等待。
5. BLOCKED
阻塞状态,线程在等待进入同步区域。
6. TERMINATED
线程执行结束,终止状态。

从上面的状态图可以看出,线程从新建、执行到结束是单向的,期间可能会经历等待和阻塞状态,线程执行结束进入终止状态后将不能再重复使用。 任何时候一个CPU核只能执行一个线程,也就是说同时并行运行的线程数与CPU核数相等。 在操作系统内核层面,线程只有分配了CPU的执行时间片,才算处于RUNNING状态。 而当有大于CPU核数的线程需要执行,没有分配到CPU执行时间片的线程则处于READY状态。 RUNNINGREADY都是线程在内核的状态,同时映射到java的RUNNABLE状态。 RUNNABLE正如其名,表示可运行的状态,并非正在运行的状态。

线程池

java编程不可避免的要使用线程,而使用线程更常见的方式是使用线程池。 说起池这个东西,我们应该比较熟悉,例如:连接池。 其实池就是一个容器,里面有一堆预先创建好的对象,我们就称其为对象池,而当这个对象具体为线程,那就是线程池了。 前面讲线程状态说过,线程执行从run()方法退出就会进入终止状态,那么这个线程就消亡了,不能再复用。 线程池的概念就是要复用线程,避免创建开销,那么如何复用呢,其实就是要让池中的线程不用从run()方法中退出。 所以为了复用线程,池的实现会与一个阻塞队列结合,空闲时线程阻塞在队列上等待任务到来,任务执行结束后再重新阻塞,永远不会退出。

jdk1.5引入了java.util.concurrent并发包后,我们可以很方便的通过ThreadPoolExecutor来创建线程池


public ThreadPoolExecutor
(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler
)

如上所示的构造方法中,corePoolSizemaximumPoolSizeworkQueue的关系一直让人容易误解。 当待执行任务数大于corePoolSize时,多出的任务请求会被放进 workQueue中等待执行,直到workQueue满了后 才会继续启动新线程直到总线程数达到maximumPoolSize的大小,其示意图如下。

 
 http://blog.csdn.net/mindfloating/article/details/22576705

程序员的视角:java 线程(转)的更多相关文章

  1. 【转载】国外程序员整理的Java资源大全

    以下转载自: 推荐!国外程序员整理的Java资源大全中文版    https://github.com/akullpp/awesome-java英文版 Java 几乎是许多程序员们的入门语言,并且也是 ...

  2. C++程序员如何转Java

     C++程序员如何转Java 忙里偷闲,到了这个时间终于得空写一篇早想写的文章.其实本文的标题有些不太准确,C++程序员写Java代码不是说就非得转行写Java,抛弃C++,而只是多了一个选择而已.两 ...

  3. 一位资深程序员大牛给予Java初学者的学习路线建议

    java学习这一部分其实也算是今天的重点,这一部分用来回答很多群里的朋友所问过的问题,那就是我你是如何学习Java的,能不能给点建议?今天我是打算来点干货,因此咱们就不说一些学习方法和技巧了,直接来谈 ...

  4. Spring MVC 程序首页的设置 - 一号门-程序员的工作,程序员的生活(java,python,delphi实战)

    body { font-family: "Microsoft YaHei UI","Microsoft YaHei",SimSun,"Segoe UI ...

  5. 转载:一位资深程序员大牛给予Java初学者的学习路线建议

    一位资深程序员大牛给予Java初学者的学习路线建议   java学习这一部分其实也算是今天的重点,这一部分用来回答很多群里的朋友所问过的问题,那就是我你是如何学习Java的,能不能给点建议?今天我是打 ...

  6. 震惊!90%的程序员不知道的Java知识!

    震惊!90%的程序员不知道的Java知识! 初学Java的时候都会接触的代码 public static void main(String[] args){ ... } 当时就像背公式一样把这行代码给 ...

  7. 今天看到的一篇文章:一位资深程序员大牛给予Java初学者的学习路线建议

    一位资深程序员大牛给予Java初学者的学习路线建议 持续学习!

  8. 什么是函数,干嘛啊,怎么干。一个py程序员的视角.md

    目录 前言 本质 math definition py definition class 是类,是对象的蓝本 回到函数 一个结论 self 是什么? 以上就是py世界里函数的定义 什么是函数,干嘛啊, ...

  9. 程序员的视角:java 线程

    在我们开始谈线程之前,不得不提下进程. 无论进程还是线程都是很抽象的概念,有一个关于进程和线程很形象的比喻能帮我们更好的理解. 进程就像个房子,房子是一个包含了特定属性的容器,例如空间大小.卧室数量等 ...

随机推荐

  1. Nginx负载均衡:分布式/热备Web Server的搭建

    Nginx是一款轻量级的Web server/反向代理server及电子邮件(IMAP/POP3)代理server.并在一个BSD-like 协议下发行.由俄罗斯的程序设计师Igor Sysoev所开 ...

  2. 编译Boost 详细步骤 适用 VC6 VS2003 VS2005 VS2008 VS2010

    vs2008编译boost [一.Boost库的介绍] Boost库是一个经过千锤百炼.可移植.提供源代码的C++库,作为标准库的后备,是C++标准化进程的发动机之一.Boost库由C++标准委员会库 ...

  3. velocity中的velocityCounter不起作用的原因

    今天用org.springframework.ui.velocity.VelocityEngineFactoryBean 时,velocityCounter这个变量的时候死活不起作用,折腾了良久也不行 ...

  4. 【玩转cocos2d-x之四十】怎样在Cocos2d-x 3.0中使用opengl shader?

    有小伙伴提出了这个问题.事实上GLProgramCocos2d-x引擎自带了.全然能够直接拿来用. 先上图吧. 使用opengl前后的对照: watermark/2/text/aHR0cDovL2Js ...

  5. NumberFormat 类

    NumberFormat 表示数字的格式化类, 即:能够依照本地的风格习惯进行数字的显示. 此类的定义例如以下: public abstract class NumberFormat extends ...

  6. 攻略三战的完美体验3Castle Fantisia阿兰·梅希亚战争艾伦西战记它包含重做版本(这是新的艾伦·梅希亚大战)

    (城堡幻想曲3,纠正大家个错误哦,不是圣魔大战3,圣魔大战是城堡幻想曲2,圣魔大战不是个系列,艾伦西亚战记==艾伦希亚战记,一个游戏日文名:タイトル キャッスルファンタジア -エレンシア戦記-リニュー ...

  7. 与众不同 windows phone (15) - Media(媒体)之后台播放音频

    原文:与众不同 windows phone (15) - Media(媒体)之后台播放音频 [索引页][源码下载] 与众不同 windows phone (15) - Media(媒体)之后台播放音频 ...

  8. perl 使用cookie

    use Net::SMTP; use LWP::UserAgent; use HTTP::Cookies; use HTTP::Headers; use HTTP::Response; use Enc ...

  9. 怎样学习java?

    嗯.不知不觉中,学习java的时间快要两年了.在学习这两年中.遇到的挫折非常多,收货的知识也非常多.以下我给出我自己在学习过程中使用到的经验.以及相关的资源链接,希望每个爱编程.爱java的人.能够有 ...

  10. IDFA的值什么时候会发生改变

    在何种情况下 , 应用的IDFA值会发生改变? 近期工作中须要获得一个能够唯一地标示每个不同应用的ID,之前的苹果UDID已经不让使用了. 那么我们须要使用新的IDFA来引用.可是在某些情况下这个ID ...