进程与线程区别
进程:我们以前在学习操作系统的时候给出进程的定义是:程序的一次执行。简单理解就是操作系统中对于数据集的一次指令运行(资源分配和调度的基本单位),是一个动态的概念,也是一个活动实体,两者并不矛盾。详情学习可参考:《现代操作系统》。
线程:是进程中执行的持有资源且被CPU调度的最小单位,一个进程可以包含一个或多个线程,那么就是说,线程是不能脱离进程而单独存在的,有人称之为轻量级进程,但是概念这个不准确,需要精确理解,就得多花时间去研究比较两者的区别。
先启动一个线程
//只有一个线程的程序,main函数启动本身就是启动一个线程,而且是主线程
public class FirstThread {
public static void main(String[] args) {
System.out.println("当前线程ID:"+Thread.currentThread().getId());
System.out.println("当前线程Name:"+Thread.currentThread().getName());
reading();
working();
}
public static void reading(){
System.out.println("I'm reading");
}
public static void working(){
System.out.println("I'm working");
}
}
创建线程的方式
有的人说,有一种:继承Thread类
有的人说,有两种:继承Thread类或实现Runnable接口
有的人说,有三种:继承Thread类,实现Runnable接口或Callable接口
有的人说,有四种:继承Thread类,实现Runnable接口,实现Callable接口或者通过线程池来创建
我信你们个鬼,你们这些糟老头子坏得很,说的都可以(开个玩笑)
我相信要是看过源码,你就知道其中的道理的,Runnable和Callable设计是相似的,Thread实现的就是Runnable接口,而Runnable中的run方法没有返回值和异常机制,而Callable中的call方法有返回值和异常机制。
接下来我们通过Thread和Runnable来实现线程的创建
/**
* 通过继承Thread类创建一个线程
*
**/
public class SecondThread extends Thread{
@Override
public void run() {
for (int i=0;i<5;i++){
System.out.println("我是自定义线程");
}
}
public static void main(String[] args) {
SecondThread thread = new SecondThread();
getThreadInfo(thread);
thread.start();//线程启动方式(启动之后线程出于就绪状态,等待获取CPU进行执行)
}
public static void getThreadInfo(Thread thread){
System.out.println("当前线程ID:"+thread.getId());
System.out.println("当前线程Name:"+thread.getName());
}
}
/**
* 通过实现Runnable接口获取一个线程单元(作为Thread的构造函数参数传入)
*
**/
public class ThirdThread implements Runnable{
@Override
public void run() {
for (int i=0;i<5;i++){
System.out.println("我是自定义线程");
}
}
public static void main(String[] args) {
Thread thread = new Thread(new ThirdThread());
getThreadInfo(thread);
thread.start();
}
public static void getThreadInfo(Thread thread){
System.out.println("当前线程ID:"+thread.getId());
System.out.println("当前线程Name:"+thread.getName());
}
}
线程的生命周期
线程的生命周期可以通过以下几种状态来进行解释(网上皆有不同状态的说法,大致都符合)
上图是用mermaid插件画的,不好看的话,可以网上找图哈。
1.New:这个状态,也就是我们在通过继承Thread类来进行创建线程还没有调用start方法的时候的一种状态,自我感觉这个状态跟创建普通的类没啥区别,知道有这么个状态就好了。
2.Runnable:这个是一个运行态(需要解释的是:这个状态包括两个状态,一个是就绪状态,一个是执行状态),这个是调用了start方法之后还没有获取CPU执行权期间的就是一种就绪的可执行的状态,这种状态不能直接切换到除RUNNING的其他状态,除非意外终止程序。
3.Running:CPU通过一定的调度算法,选中执行该线程后的一种状态,这个时候才会真正执行run方法里面的程序逻辑,同样的这个RUNNING状态也是一种RUNNABLE状态,反之就不是。
4.Blocked:遇到一些资源请求失败或者阻塞指令的情况下进入的一种阻塞状态,这个状态下一旦获取到资源或者阻塞指令执行结束,则会进入到就绪状态,等待执行。
5.Dead(Terminated):这个就是线程的最终状态,意味着线程生命走到了尽头,可能是寿终正寝,也可能是中途夭折,这种状态不会切换到其他任何一种状态了。
状态转换说明
Running状态下的转换
- 进入Dead状态,调用了jdk不推荐使用的
stop方法或者判断某个逻辑标识 - 进入Blocked状态,调用sleep,wait,join或者进入io阻塞或着同步块阻塞等
- 进入Runnable状态,cpu轮询放弃执行或者主动调用yield方法放弃执行。
Blocked状态下的转换
- 进入Dead状态,调用了jdk不推荐使用的
stop方法或者意外死亡 - 进入Runnable状态,阻塞操作结束,休眠结束,wait的线程被唤醒,获取到某个资源,阻塞过程被打断等。
以上是对线程的基本情况做了大致的介绍。