Java与线程

标签: 并发
60人阅读 评论(0) 收藏 举报
分类:

线程的实现

1. 使用内核线程(Kernel-Level Thread)实现

内核线程是由操作系统内核直接支持的线程,由内核完成线程切换,通过操纵调度器进行调度,内核负责将线程任务映射到处理器上。程序并不会直接使用内核线程,而是使用其提供的高级借口,即轻量级进程(Light Weight Process)。因为LWP和KLT是1:1的关系,所以这种实现被称为一对一的线程模型

这一实现方式的优点在于每一个LWP都是一个独立的调度单元,即使阻塞也不会影响整个进程继续工作。缺点在于各种线程操作需要系统调用,代价较高,而且每一个LWP对应的KLT都要消耗一定的内核资源,所以对系统支持的线程数量受到限制。

2. 使用用户线程(User Thread)实现

非内核线程即可认为是用户线程,广义上来讲LWP也算是用户线程,此处描述的是狭义上所指的用户线程,即完全在用户空间内实现的线程。这种实现种进程与线程对应关系为1:N,所以被称为一对多的线程模型。

这一实现方式有点在于不需要在用户态和内存态之间切换,所以操作迅速消耗较低,也支持更大规模的线程数量。缺点在于没有内核的支持,所有的线程调度由用户程序自己处理,复杂性很高,甚至无法实现。Java语言现在已经不使用这个方式了。

3. 使用用户线程加轻量级进程混合实现

混合以上两种实现方式,用户线程负责各类处理操作,LWP负责协调用户线程到KLT之间的映射,用户线程与LWP之间比例不确定,是多对多的线程模型

4. Java线程的实现

Java线程在Windows和Linux种使用一对一的线程模型,而在Solaris平台中,可以使用-XX:+UseLWPSynchronization(默认,限于Solaris平台)选择多对多的线程模型,或者-XX:+UseBoundThreads(限于Solaris平台)选择一对一的线程模型。


Java线程调度

线程的调度有两种方式,一种是协同式调度,一种是抢占式调度。

协同式调度是指线程执行时间由自身控制,执行完成后通知系统切换线程。这种方式实现简单,但执行时间不可控制,如果线程的执行出现问题,系统无法得知,程序会始终阻塞在那里。

抢占式调度是指线程执行时间由系统分配,线程的切换也由系统控制,不会出现协同式调度中的持续阻塞问题。Java使用的就是这种方式。

Java语言中定义了10个级别的线程优先级,但是一方面由于需要映射到系统原生的线程优先级,可能出现不同Java线程优先级映射结果相同,一方面系统也不能完全保证一定按照线程优先级来决定优先分配执行时间,所以并发编程时不能完全依赖于Java提供的线程优先级。


状态转换

Java语言定义了5种线程状态,任意时间点一个线程只能处于其中一种状态。

新建(New): 创建线程后未启动。

运行(Runnable): 这一状态包括了操作系统线程状态的Running和Ready。

等待(Waiting): 处于这一状态的不会被分配CPU执行时间直到被唤醒。等待分为两种:

  • 无限期等待:需要等待其他线程显式唤醒,调用无参的Object.wait(),无参的Thread.join(),LockSupport.park()方法进入这一状态
  • 限期等待(Timed Waiting): 不需要等待其他线程显式唤醒,等待一定时间后由系统自动唤醒,调用Thread.sleep(),有Timeout参数的Object.wait(),有Timeout参数的Thread.join(),LockSupport.parkNanos(),LockSupport.parkUntil()方法进入这一状态

阻塞(Blocked): 阻塞状态也不会被分配CPU执行时间,与等待不同的是,阻塞状态的线程等待着获取一个排他锁,而不是时间或者唤醒动作,程序运行至同步区域的时候线程会进入这一状态。

结束(Terminated): 线程结束执行。


参考文献:

《深入理解Java虚拟机:JVM高级特性与最佳时间》,第2版,周志明


查看评论
    个人资料
    持之以恒
    等级:
    访问量: 436
    积分: 59
    排名: 166万+
    文章分类
    文章存档