本篇文章给大家带来的内容是关于java中线程如何实现?java线程的实现方法(图文),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。
进程与线程
在传统的操作系统中,最核心的概念是“进程”,进程是对正在运行的程序的一个抽象。
进程的存在让“并行”成为了可能,在一个操作系统中,允许运行着多个进程,这些进程“看起来”是同时在运行的。
如果我们的计算机同时运行着 web 浏览器、电子邮件客户端、即时通讯软件例如qq微信等多个进程,我们感觉这些进程都是同时在运行的,假设这台计算机搭配的是多个 cpu 或者 多核 cpu,那么这种多个进程并行的现象可能一点也不奇怪,完全可以为每个进程单独分配一个 cpu,这样就实现了多进程并行。
然而事实上,在计算机只有一个 cpu 的情况下,它也能给人类一种感觉:多个进程同时在运行。但人类的感觉往往是比较模糊的,不精确的。事实是由于 cpu 的计算速度非常地快,它能快速地在各个进程之间切换,在某一瞬间,cpu 只能运行一个进程,但一秒钟之内,它就能通过快速切换,让人产生多个进程同时在运行的错觉。
在操作系统中,为什么在进程的基础上,又衍生出了线程的概念呢?
由于对于一些进程而言,它内部会发生多种活动,有些活动可能会在某个时间里阻塞,有些活动不会,如果通过线程将这些活动分离开使它们能够并行地运行,则设计程序的时候会更加简单。
线程比进程的创建更加轻量级,性能消耗更少
如果一个进程既需要 cpu 计算,也需要i/o处理,拥有多线程允许这些活动重叠进行,加快整个进程的执行速度。
每一个进程在操作系统中都拥有独立的一块内存地址空间,该进程创建的所有线程共享这块内存,支持多线程的操作系统,会让线程作为 cpu 调度的最小单位。cpu 的时间片在不同的线程之间进行分配。
线程的可能实现方式基本上主流的操作系统都支持线程,也提供了线程的实现。而 java 语言为了应对不同硬件和操作系统的差异,提供了对线程操作的统一抽象,在 java 中我们使用 thread 类来代表一个线程。
thread 的具体实现可能会有不同的实现方式:
使用内核线程实现内核线程是操作系统内核支持的线程,在内核中有一个线程表用来记录系统中的所有线程,创建或者销毁一个线程时,都需要涉及到系统调用,然后再内核中对线程表进行更新操作。对内核线程的阻塞以及其它操作,都涉及到系统调用,系统调用的代价都比较大,涉及到在用户态和内核态之间的来回切换。此外,内核内部有线程调度器,用于决定应该将 cpu 时间片分配个哪个线程。
程序一般不会直接操作内核线程,而是使用内核线程的一种高级接口,轻量级进程。轻量级进程与内核线程之间的关系是 1:1,每一个轻量级进程内部都有一个内核线程支持。
上图中, lwp 指 light weight process,即轻量级进程;klt 指 kernel level thread,即内核线程。
使用用户线程实现用户线程是程序或者编程语言自己实现的线程库,系统内核无法感知到这些线程的存在。用户线程的建立、同步、销毁和调度,都在用户态中完成,无须内核的帮助,不需要进行系统调用,这样的好处是对于线程的操作是非常高效的。在这种情况下,进程和用户线程的比例是 1 :n。
用户态线程面对如何阻塞线程时,会面临困难,阻塞一个用户态线程会出现把整个进程都阻塞的情况,多线程也就失去了意义。因为缺少内核的支持,所以很多需要利用内核才能完成的工作,例如阻塞与唤醒线程、多 cpu 环境下线程的映射等,都需要用户程序去实现,实现起来会异常困难。
使用用户线程和内核线程混合实现在这种混合实现下,既存在用户线程,也存在内核线程。用户态线程的创建、切换这些操作依然很高效,并且用户态实现的线程,比较容易加大线程的规模。需要操作系统内核支持的功能,则通过内核线程来做到,例如映射到不同的处理器上、处理线程的阻塞与唤醒以及内核线程的调度等。这种实现依然会使用到轻量级进程 lwp,它是用户线程和内核线程之间的桥梁。
java 线程的实现在 jdk1.2 之前, java 的线程是使用用户线程实现的,在 jdk1.2 之后,java 才采用操作系统原生支持的线程模型来实现,java 线程模型的实现方式,主要取决于操作系统。对于 oracle jdk 来说,在 windows 和 linux 上的线程模型是采用一对一的方式实现的,即一条 java 线程映射到一条轻量级进程(内核线程)。而在 solaris 平台中,java 则支持 1 :1 和 n : m 的线程模型。
线程的阻塞与等待java 中的线程的状态有以下几种:new,runnable,waiting, timedwaiting, blocked,terminated。
new:线程初创建,未运行
runnable:线程正在运行,但不一定消耗 cpu
blocked:线程正在等待另外一个线程释放锁
waiting:线程执行了 wait, join, locksupport.park() 方法
timed_waiting:线程调用了sleep, wait, join, locksupport.parknanos() 等方法,与 waiting 状态不同的是,这些方法带有表示时间的参数。
其中 blocked 和 waiting 有个重要的区别是,阻塞(blocked)状态的线程在等待获取一个排他锁,例如线程在等待进入一个synchronized关键字包围的临界区时,就进入 blocked 状态。而 waiting 状态则是在等待被唤醒,或者等待一段时间。
以上就是java中线程如何实现?java线程的实现方法(图文)的详细内容。