并发编程-快速理解线程

进程与线程区别

进程:我们以前在学习操作系统的时候给出进程的定义是:程序的一次执行。简单理解就是操作系统中对于数据集的一次指令运行(资源分配和调度的基本单位),是一个动态的概念,也是一个活动实体,两者并不矛盾。详情学习可参考:《现代操作系统》。

线程:是进程中执行的持有资源且被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());
    }
}

线程的生命周期

线程的生命周期可以通过以下几种状态来进行解释(网上皆有不同状态的说法,大致都符合)

start
get cpu
blocking event
unblock
yield/cpu not time
exception/done
New
Runnable
Running
Blocked
Dead

上图是用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的线程被唤醒,获取到某个资源,阻塞过程被打断等。

以上是对线程的基本情况做了大致的介绍。

猜你喜欢

转载自blog.csdn.net/weixin_30484149/article/details/86327384
今日推荐