1 基础概念
1.1 线程和进程
进程是应用程序的执行路径(执行过程),是操作系统分配资源的最小单元。一个线程内部可以有一个或多个线程,会共享这个进程的资源。
线程是进程中负责程序执行的执行单元,是操作系统调度的最小单元。
一个进程中至少有一个线程,同时运行多个线程的程序称为多线程程序。进程让操作系统的并发成为可能,而线程让进程的内部并发成为可能。
1.2并发和并行
并行:两个或多个事件在同一时刻发生,如双核CPU在同一时刻可以同时处理两条指令。
并发:指两个或多个事件在同一个时间间隔内发生。这些事件宏观上是同时发生的,但是微观上是交替发生的。如由于一个单核CPU同一时刻只能执行一个程序,因此操作系统会负责协调多个程序交替执行(这些程序微观上是交替执行的,但是宏观上看起来是同时执行的)。
并发可以简单理解为单位时间内处理事情的能力,如对于单核CPU,如果处理一条命令的时间为10ms,忽略线程间切换的时间,并发能力就是100.
1.3 并发编程的优缺点
优点:充分利用CPU资源,提高响应速度。
缺点:对共享资源可能会存在冲突,存在死锁的可能性等。
2 Java线程调度
线程调度是指系统为线程分配处理器使用权的过程,主要调度方式有两种,分别是协同式线程调度(Cooperative Threads-Scheduling)和抢占式线程调度(Preemptive Threads-Scheduling)。
1.1协同式线程调度
协同式线程调度线程的执行时间由线程本身来控制,线程把自己的工作执行完了之后,要主动通知系统切换到另外一个线程上。
这种调度方式有一个很大的缺点:如果某个线程由于某种原因阻塞或者线程编写有问题,这会导致该线程不会交出CPU的执行权,进而导致整个进程阻塞最后导致系统崩溃。
1.2 抢占式线程调度
抢占式线程调度每个线程将由系统来分配执行时间,线程的切换不由线程本身来决定。在这种实现线程调度的方式下,线程的执行时间是系统可控的,也不会有一个线程导致整个进程阻塞的问题。
Java使用的线程调度方式就是抢占式调度。
3 线程的状态
线程状态: New(新创建)、Runnable(可运行)、Blocked(被阻塞)、Waiting(等待)、Timed waiting(计时等待)、Terminated(被终止)
3.1 新创建(New)
创建后尚未启动的线程处于这种状态。线程创建之后,不会立即进入可运行状态,因为线程的运行需要一些条件(如程序计数器、Java栈、本地方法栈等内存资源都是线程私有的,所以需要为线程分配一定的内存空间),只要线程运行需要的所有条件满足了,才进入就绪状态。
3.2 可运行(Runnable)
一旦调用start方法,线程处于可运行状态。当进程进入可运行状态,不代表就能获取CPU执行权,只有得到CPU的执行权,线程才真正进入运行(Running)状态。一旦一个线程开始运行,它不必始终保持运行。事实上,运行中的线程被中断,目的是为了让其他线程获得运行机会。线程调度的细节依赖于操作系统提供的服务。抢占式调度系统给每个可运行线程一个时间片执行任务。当时间片用完,操作系统剥夺该线程的运行权,并给另一个线程运行机会。当选择下一个线程时,操作系统会考虑线程的优先级。
 在具有多个处理器的机器上,每一个处理器运行一个线程,可以有多个线程并行运行。当然,如果线程数多于处理器的数目,调度器依然采用时间片机制。
3.3 无限期等待(Waiting)
处于这种状态的线程不会被分配CPU执行时间,它们要等待被其他线程显式地唤醒。以下方法会让线程陷入无限期的等待状态:
(1) 没有设置Timeout参数的Object.wait()方法。
(2) 没有设置Timeout参数的Thread.join()方法。
(3) LockSupport.park()方法。
3.4 限期等待(Timed Waiting)
处于这种状态的线程也不会被分配CPU执行时间,不过无须等待被其他线程显式地唤醒,在一定时间之后它们会由系统自动唤醒。如以下的方法会使得线程处于Timed Wating状态:
(1) Thread.sleep(long milis)方法。
(2) 没设置了Timeout参数的Object.wait()方法和Thread.join()方法。
(3) LockSupport.parkNanos()方法。
..
3.5 阻塞(Blocked)
在程序等待进入同步区域的时候,线程将进入这种状态。“阻塞状态”与“等待状态”的区别是:“阻塞状态”在等待着获取到一个排他锁的过程,而“等待状态”则是线程等待一段时间等待被其他线程唤醒或者等待时间结束的过程。
3.6 终止(Terminate)
已终止线程的线程状态,线程已经结束执行。当线程任务执行结束或者线程因为一个未捕获的异常会导致线程终止。
下图表示线程状态的转换
4 上下文的切换
多线程程序会共同使用计算机上的CPU,而线程数量大于给程序分配的CPU数量时,为了让各个线程都有机会获取到CPU执行权,就需要轮转使用CPU。不同此案城切换使用CPU发生的切换数据就是上下文切换。
由于切换时当前线程的任务还没有执行完毕,所以为了让该线程再次获取到CPU执行权时能从切换之前的状态继续执行,所以在上下文切换时需要保存线程的运行状态。需要记录的数据有:程序计数器和CPU寄存器的状态。
注:本文基本都是些基础概念,就不总结了。
本文完
Redis专题的内容已经汇总:Redis专题,后续还会更。。。。
注:本文部分内容参考:《Java核心技术I》《深入理解Java虚拟机 第2版》,如发现错误,请指正。
网友评论