多线程详解(二)

来源:互联网 发布:互联网 中文域名 编辑:程序博客网 时间:2024/06/14 07:11

多线程详解(二)

在正式介绍线程创建的第二种方法之前,我们接着多线程详解(一),讲一下:对线程的内存图、线程的状态,为下面的学习打下基础,小伙伴们不要急哟!!

一、多线程运行的内存图(ps.博主没有找到合适的画图工具,欢迎大神们贡献啊)

class person extends Thread{       int i;    private String name;    person(String name)    {//给线程命名,使用super        super(name);        this.name=name;    }    public void run()    {        show();    }    public void show()    {        for(i=0;i<=10;i++)        {            System.out.println(name+"x="+i+"...name="+Thread.currentThread().getName());        }    }}public class demo1{public static void main(String[] args){    person p1=new person("zhangsan");    person p2=new person("lisi");    p1.start();    p2.start();    System.out.println("出来吧,name="+Thread.currentThread().getName());}}

每开启一条线程就多一条运行路径,所以以上述代码为例——共有三条路径

    (1)main线程路径
    (2)run线程路径
    (3)另一条run线程路径

1、每一条路径上独立的进行压栈弹栈,每一条线程上都要自己的栈区,所以互相不影响

2、当一条路劲上的代码运行完,该线程自动结束

3、当main线程提前运行结束后,其他两条线程依旧运行。所以可以看出每条线程之间是完全独立的,当一条线程有异常时,其他的线程继续运行不受影响。

4、为多线程安全分析埋下伏笔(这里不详细讲多线程安全了,以后会更行的哦)

二、线程的状态(重点)

1、运行(使用start方法):具备执行资格和执行权

2、消亡(任务运行完了,自动消亡OR使用stop方法强制消亡):既不具备执行资格也不具备运行资格

3、冻结:

《1》使用sleep(time)方法———时间到了,自动恢复————既不拥有执行权也不拥有执行资格(释放执行权的同时释放执行资格)

《2》使用wait()方法————使用notify()方法唤醒————既不拥有执行权也不拥有执行资格(释放执行权的同时释放执行资格))

解释:如果一个线程处于运行状态,那么CPU就具备执行资格和拥有CPU的执行权,那么什么是执行权?什么又是执行资格呢??

《1》CPU执行资格:CPU可以处理,在CPU的队列中排队等待处理

《2》CPU执行权:正在被CPU处理

下面我们通过举一个例子,解释一下什么是临时阻塞

假设A、B、C、D四个线程都处于执行状态,当A运行的时候,A具备执行资格和执行权,此时其他三个具备执行资格但正在等待执行权,这种状态就是临时阻塞状态

所以,临时阻塞状态———具备执行资格但是不具备执行权

在一个时刻,只有一个线程拥有执行权,而其他运行的线程都是临时阻塞的。

辨析:临时阻塞状态和冻结状态的区别
    (1)执行资格和执行权的不同
    (2)冻结是由程序员控制的而临时阻塞是cpu运行控制的
    (3)临时阻塞的时间往往是极短的,但是冻结的时间是自定义的,相比较而言是长的。
    (4)临时阻塞是运行时的一种状态,所以冻结唤醒后,必须经过运行才能有时间阻塞。

三、创建线程的第二种方式

1、继承Thread类创建方法的局限性

如果原来的类已经继承了其他类,使用第一种方式继承Thread,就存在了多继承,在java中是不允许的

例如:

class person extends fu{   int i;    private String name;    person(String name)    {           super(name);        this.name=name;    }    public void run()    {        show();    }    public void show()    {        for(i=0;i<=10;i++)        {            System.out.println(name+"x="+i+"...name="+Thread.currentThread().getName());        }    }}public class demo1{public static void main(String[] args){    person p1=new person("zhangsan");    person p2=new person("lisi");    p1.start();    p2.start();    System.out.println("出来吧,name="+Thread.currentThread().getName());}}

但是在这种情况下,如果你依旧要使用继承Thread的方法创建线程也是可以的,就是利用再增加一层继承的方法来实现。示例如下:

class fu extends Thread{     int i;    private String name;    person(String name)    {        super(name);        this.name=name;    }    public void run()    {        show();    }    public void show()    {        for(i=0;i<=10;i++)        {            System.out.println(name+"x="+i+"...name="+Thread.currentThread().getName());        }    }}class person extends fu{      }public class demo1{public static void main(String[] args){    person p1=new person("zhangsan");    person p2=new person("lisi");    p1.start();    p2.start();    System.out.println("出来吧,name="+Thread.currentThread().getName());}}

从上面的示例我们可以看出,多增加一层继承关系,其实是开发者强制加上去的,而且实现过程也很繁琐,所以我们应该抛弃此方法,重新思考……..

2、使用接口的方法创建线程

我们现在的目的是让person类创建一个线程,但是person类不可以再继承。由于需要扩展person类功能,所有我们很自然的想到使用————接口

创建步骤:

    《1》定义类实现Runnable接口
    《2》覆盖接口中的run方法,将线程的任封装到run方法中
    《3》通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数进行传递。
    《4》调用线程对象的start方法开启线程

为什么第三步中,要将Runnable接口的子类对象作为Thread类的构造函数进行传递呢?

因为,线程的任务都是封装在Runnable接口子类对象的run方法中。所以要在线程对象创建时就明确要运行的任务。

示例代码如下:

//实现Runable接口,class person implements Runnable{   int i;    private String name;    person(String name)    {        this.name=name;    }//  覆盖run方法    public void run()    {        show();    }    public void show()    {        for(i=0;i<=10;i++)        {            System.out.println(name+"x="+i+"...name="+Thread.currentThread().getName());        }    }}public class demo1{public static void main(String[] args){//这个不是线程对象//  person p1=new person("zhangsan");//  person p2=new person("lisi");//这个是线程对象    person p=new person("zhangsan");    Thread t1=new Thread(p);    Thread t2=new Thread(p);    t1.start();    t2.start();    System.out.println("出来吧,name="+Thread.currentThread().getName());}}

运行结果:

这里写图片描述

关于多线程创建的第二种方法的详细介绍,请阅读多线程详解(三)

0 0
原创粉丝点击