进程与线程
- 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,即进程空间或(虚空间)。进程不依赖于线程而独立存在,一个进程中可以启动多个线程。
- 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。
- 每个进程都有一段专用的内存区域,而线程间可以共享相同的内存区域(包括代码和数据),并利用这些共享单元来实现数据交换、实时通信与必要的同步操作。
- 线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间
在Java中,每次程序运行至少启动2个线程:一个是main线程,一个是垃圾收集线程。因为每当使用java命令执行一个类的时候,实际上都会启动一个JVM,每一个JVM实际上就是在操作系统中启动了一个进程。
线程的创建
Java种线程有三种创建方式
- 实现Runnable接口
- 继承Thread类
- 通过 Callable 和 Future 创建线程。
线程的优先级
- 每一个 Java 线程都有一个优先级,这样有助于操作系统确定线程的调度顺序。
- Java 线程的优先级是一个整数,其取值范围是 1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY )。
- 默认情况下,每一个线程都会分配一个优先级 NORM_PRIORITY(5)。
- 具有较高优先级的线程对程序更重要,并且应该在低优先级的线程之前分配处理器资源。但是,线程优先级不能保证线程执行的顺序,而且非常依赖于平台。
线程的状态和生命周期
简单生命周期
复杂生命周期
线程的运行状态
当需要新起一个线程来执行某个子任务时,就创建了一个线程。但是线程创建之后,不会立即进入就绪状态,因为线程的运行需要一些条件(比如内存资源),只有线程运行需要的所有条件满足了,才进入就绪状态。
当线程进入就绪状态后,不代表立刻就能获取CPU执行时间,也许此时CPU正在执行其他的事情,因此它要等待。当得到CPU执行时间之后,线程便真正进入运行状态。
线程在运行状态过程中,可能有多个原因导致当前线程不继续运行下去,比如用户主动让线程睡眠(睡眠一定的时间之后再重新执行)、用户主动让线程等待,或者被同步块给阻塞,此时就对应着多个状态:time waiting(睡眠或等待一定的时间)、waiting(等待被唤醒)、blocked(阻塞)。
线程的方法
方法 | 描述 |
---|---|
setName | 设置线程的名字 |
getName | 获取线程的名字 |
setPriority | 设置线程的优先级 |
getPriority | 获取线程的优先级 |
setDaemon | 设置线程是否为守护线程 |
isDaemon | 判断当前线程是否为守护线程 |
isAlive() | 判断当前线程是否处于活动状态(活动状态就是已经启动尚未终止) |
currentThread | 获取当前的线程 |
interrupt | 其作用是中断此线程(此线程不一定是当前线程,而是指调用该方法的Thread实例所代表的线程),但实际上只是给线程设置一个中断标志,线程仍会继续运行。 |
interrupted | 作用是测试当前线程是否被中断(检查中断标志),返回一个boolean并清除中断状态,第二次再调用时中断状态已经被清除,将返回一个false。 |
isInterrupted | 作用是只测试此线程是否被中断 ,不清除中断状态 |
join | 无参时表示让父线程等待子线程结束之后才能继续运行;有参数表示让父线程等待子线程指定的时间后继续运行 |
sleep | 使当前线程睡眠至少多少毫秒(尽管它可能在指定的时间之前被中断),线程睡眠到期自动苏醒,并返回到可运行状态,不是运行状态,不能保证该线程睡眠到期后就开始执行。 |
yield | 让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会,这个暂停会放弃cpu资源,放弃的时间不确定。目的是让相同优先级的线程之间能适当的轮转执行。但是,实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。 |
- sleep方法不会释放锁
- yield方法让当前线程交出CPU权限,只能让拥有相同优先级的线程有获取CPU执行时间的机会;同样不会释放锁。调用yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时间
- join方法会使调用所在的线程进入等待(阻塞)状态,调用join方法实际上是调用了Object的wait方法;由于wait方法会让线程进入阻塞状态,并且会释放线程占有的锁,并交出CPU执行权限,所以join方法也会释放锁。
- interrupt()方法不能中断正在运行过程中的线程,只能中断阻塞过程中的线程。
线程的分类
Java中线程分为两种,一种是用户线程,一种是守护线程。默认创建的线程是用户线程,可以在调用start()方法之前通过setDaemon(true)方法来设置为守护线程。
两者的区别
- 用户线程在主线程结束后还会继续运行,JVM存活。
- 如果没有用户线程,只有守护线程,那么在主线程结束后,JVM也将结束。
- 守护线程的优先级比较低,用于为系统中的其它对象和线程提供服务。
- 守护线程创建的其他线程默认也是守护线程
- 垃圾回收线程是一个经典的守护线程,除此之外单元测试的线程应该也是一个守护线程。
静态变量是ClassLoader级别的,如果Web应用程序停止,这些静态变量也会从JVM中清除。但是线程则是JVM级别的,如果你在Web 应用中启动一个线程,这个线程的生命周期并不会和Web应用程序保持同步。也就是说,即使你停止了Web应用,这个线程依旧是活跃的。正是因为这个很隐晦的问题,所以很多有经验的开发者不太赞成在Web应用中私自启动线程。