进程与线程
- 程序(Program)是为实现特定目标或解决特定问题而用计算机语言(比如Java语言)编写的命令序列的集合
进程
- 进程(process)指一个程序的一次执行的过程。
打开win10的任务管理器可以看见正在运行的进程
线程
- 线程与进程类似(thread)又称为轻量级进程,线程是一个程序中实现单一功能的一个指令序列,是一个程序的单个执行流,存在于进程中,是进程的一部分。
从win10任务管理器中可以查看当前进程中的线程数
进程与线程的区别
- 一个进程中可以有多个线程。线程必须依托在进程之中执行,如果多个线程在同一个进程中,则线程之间采用抢占式运行(哪个线程抢到CPU的执行权那个线程就运行,但是由于CPU在线程之间切换的很快,所以有时候感觉是多个线程是同时执行的)。当进程结束的之后,线程也跟着结束了,在Java中若一个进程中没有可执行的非守护线程,进程也将结束。
- 在Java中的进程多个线程可以共享进程的堆和方法区,但每个线程有自己的程序计数器、虚拟机栈和本地方法栈。
- 操作系统将进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位,由于线程相对进程来说基本上不拥有系统资源,故对线程的调度所付出的开销就会小得多,能更高效的提高系统中多个进程间并发执行的速度
如何创建线程
- 创建线程的方式主要的方式有两种,分别为1.继承Thread类,2.实现Runnable接口
第一种方式
- 继承Thread类,重写run方法,创建该类的对象并调用start方法
class TimeThread extends Thread {
@Override
public void run() {
while(true) {
System.out.println(new Date());
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
测试类
public class Test1 {
public static void main(String[] args) {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
TimeThread timeThread1 = new TimeThread();
timeThread1.start();
TimeThread timeThread2 = new TimeThread();
timeThread2.start();
}
}
执行测试类的主方法,在任务管理器中可以看到多出来一个javaw.exe进程线程,该进程中的线程为18,程序中开启了两个线程,程序执行时先等待10s,然后控制台每隔一秒输出两条时间信息,任务管理器中该javaw.exe进程的线程数增加了2变成了20
第二种方式
- 实现Runnable接口,实现run方法,并把该实现类的对象传入到Thread构造方法中,再调用Thread对象的start方法
class Time implements Runnable {
@Override
public void run() {
while (true) {
System.out.println(new Date());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
测试类
public class Test2 {
public static void main(String[] args) {
try {
Thread.sleep(10000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
Time time = new Time();
Thread thread = new Thread(time);
thread.start();
}
}
执行程序之后,任务管理器出现了一个新的进程javaw.ext,等待10s之后该进程的线程会增加一
注意
- Thread也同样实现了Runnable方法,由于Java的单继承特性,但是可以实现多个接口,还是实现Runnable这种方式更为灵活一些
- 创建线程之后想要开启线程需要调用start方法
- Thread类其实也是实现类Runnable接口的类
线程的生命周期
- Java中,线程有5种不同状态,分别是:新建(New)、就绪(Runable)、运行(Running)、阻塞(Blocked)和死亡(Dead)。
- 新建状态(New):新创建了一个线程对象。
- 就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
- 运行状态(Running):就绪状态的线程获取了CPU使用权,执行程序代码。
- 阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
- 等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。(wait会释放持有的锁)
- 同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
- 其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。(注意,sleep是不会释放持有的锁)
- 死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
各个状态在代码中的体现
public class Test1 {
public static void main(String[] args) {
//新建
TimeThread thread = new TimeThread();
//thread线程就绪,开始和主线程争夺CPU控制权
thread.start();
//main线程获得CPU使用权
}
//main线程死亡
}
class TimeThread extends Thread {
//thread线程获得CPU使用权
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
try {
//阻塞
sleep(1000);
//thread线程运行
//因为mian线程已经死亡,不会和thread线程
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//thread线程死亡
}