并发不一定要依赖多线程(如PHP中很常见的多进程并发),但是在Java里面谈论并发,大多数都与线程脱不开关系。
线程是比进程更轻量级的调度执行单位,线程的引入,可以把一个进程的资源分配和执行调度分开,各个线程既可以共享进程资源(内存地址、 文件I/O等),又可以独立调度(线程是CPU调度的基本单位)。 对java而言,每个已经执行start()且还未结束的java.lang.Thread类的实例就代表了一个线程。 线程的大部分方法是用native方法实现的。 实现线程主要有3种方式:使用内核线程实现、 使用用户线程实现和使用用户线程加轻量级进程混合实现。
直接由操作系统内核(Kernel,下称内核)支持的线程,这种线程由内核来完成线程切换,内核通过操纵调度器(Scheduler)对线程进行调度,并负责将线程的任务映射到各个处理器上。 程序一般不会直接去使用内核线程,而是去使用轻量级进程(Light Weight Process,LWP),即通常意义上线程,由于每个LWP都由一个内核线程支持,因此只有先支持内核线程,才能有轻量级进程。
优点:LWP相互独立,即使有一个阻塞,也不影响整体工作 缺点:完成工作需要在内核态和用户态之间相互切换,同时需要消耗内核资源,一个内核可以支持的LWP有限。
用户线程指的是完全建立在用户空间的线程库上,系统内核不能感知线程存在的实现。 用户线程的建立、 同步、 销毁和调度完全在用户态中完成,不需要内核的帮助。
优点:快速且低消耗,支持更大的线程数量。 缺点:线程的创建、 切换和调度都需要自己解决,而且由于操作系统只把处理器资源分配到进程,那和处理器相关的问题将会很难解决甚至不可能完成。
用户线程建立在用户空间中,因此用户线程的创建、 切换、 析构等操作依然廉价,并且可以支持大规模的用户线程并发。 而操作系统提供支持的轻量级进程则作为用户线程和内核线程之间的桥梁,这样可以使用内核提供的线程调度功能及处理器映射,并且用户线程的系统调用要通过轻量级线程来完成,大大降低了整个进程被完全阻塞的风险。
在目前的JDK版本中,操作系统支持怎样的线程模型,在很大程度上决定了Java虚拟机的线程是怎样映射的。对于Sun JDK来说,它的Windows版与Linux版都是使用一对一的线程模型实现的,一条Java线程就映射到一条轻量级进程之中,因为Windows和Linux系统提供的线程模型就是一对一的。而在Solaris平台中,由于操作系统的线程特性可以同时支持一对一及多对多。
线程调度是指系统为线程分配处理器使用权的过程,主要调度方式有两种,分别是协同式线程调度(Cooperative Threads-Scheduling)和抢占式线程调度(Preemptive ThreadsScheduling)。
协同式调度的多线程系统,线程的执行时间由线程本身来控制,线程把自己的工作执行完了之后,主动通知系统切换到另外一个线程上。 优点:实现简单,而且由于线程要把自己的事情干完后才会进行线程切换,切换操作对线程自己是可知的,所以没有线程同步的问题。 缺点:线程执行时间不可控制,甚至如果一个线程有问题,一直不告知系统进行线程切换,那么程序就会一直阻塞。
如果使用抢占式调度的多线程系统,那么每个线程将由系统来分配执行时间,线程的切换不由线程本身来决定。 Java使用的线程调度方式就是抢占式调度。 优点:线程的执行时间是系统可控的,也不会有一个线程导致整个进程阻塞的问题。 缺点:需要进行线程同步。
新建(New):创建后尚未启动的线程处于这种状态。 运行(Runable):Runable包括了操作系统线程状态中的Running和Ready,也就是处于此状态的线程有可能正在执行,也有可能正在等待着CPU为它分配执行时间。 无限期等待(Waiting):处于这种状态的线程不会被分配CPU执行时间,它们要等待被其他线程显式地唤醒。 以下方法会让线程陷入无限期的等待状态: ●没有设置Timeout参数的Object.wait()方法。 ●没有设置Timeout参数的Thread.join()方法。 ●LockSupport.park()方法。 限期等待(Timed Waiting):处于这种状态的线程也不会被分配CPU执行时间,不过无须等待被其他线程显式地唤醒,在一定时间之后它们会由系统自动唤醒。 以下方法会让线程进入限期等待状态: ●Thread.sleep()方法。 ●设置了Timeout参数的Object.wait()方法。 ●设置了Timeout参数的Thread.join()方法。 ●LockSupport.parkNanos()方法。 ●LockSupport.parkUntil()方法。 阻塞(Blocked):线程被阻塞了,“阻塞状态”与“等待状态”的区别是:“阻塞状态”在等待着获取到一个排他锁,这个事件将在另外一个线程放弃这个锁的时候发生;而“等待状态”则是在等待一段时间,或者唤醒动作的发生。 在程序等待进入同步区域的时候,线程将进入这种状态。 结束(Terminated):已终止线程的线程状态,线程已经结束执行