关于线程,在JVM开启后自然要有一个执行路径--------主线程,肯定就是由系统/JVM创建开启的(不然程序怎么执行)

创建线程

创建线程的需求环境:当在执行主线程时,遇到循环导致在指定位置停留太久。此时就需要创建并开启线程

(关于创建线程的思想引入理解:

假设处于需求环境中,我们要明确,1.多线程并不能缩短时间,因为CPU在某一时刻只能执行一个任务,开启的线程越多效率会越多,因为CPU切换会有时间消耗。多线程开启的意义,1.程序上,提供同时打开多个任务的“便利错觉”,2.在内存上,创建一个路径,为CPU提供切换路径

如果按照只有一个线程,在前面遇到长时间的循环,那么会导致后面的程序长时间无法执行

例如:

在运行QQ时如果只有一个主线程(聊天在游戏之前),那么只有我们结束聊天。结束聊天这个程序,才能进入游戏。但是如果是使用多线程,当对方在忙没有与你对话时,不用结束聊天这个程序,我们还是可以打开游戏

多线程的意义还是很大的)

在程序中我们看见的只有主线程,而主线程是由系统创建的(系统为程序开辟空间,CPU进行处理。都是依赖于系统)

创建线程的方法一:

1.继承Thread

2.重写Thread 中的Run 方法(public  void Rund())

3.创建线程对象

4.调用start 方法,开启线程路径,并使JVM调用重写的Run  方法(一个线程对象对应了一条路径/线程)

package Thread;
class Demo extends Thread{
    private String name;
    Demo(String name){
        this.name=name;
    }
   public  void run(){
        for(int i=1;i<=20;i++){
            System.out.println("name : "+name+Thread.currentThread().getName()+"  "+i);
        }
    }
}
public class ThreadDemo1 {
    public static void main(String[] args){
        Demo D1 = new Demo("Liu");
        Demo D2 = new Demo("Li");
        D2.start();
        D1.run();
    }

java什么时候要创建线程 什么时候需要创建线程_创建线程

 

注意:

线程对象调用run方法和调用start 的区别:

一个是简单的对象调用方法,一个是开启线程并调用run方法

1.    在主函数中创建线程对象,不开启线程就不会有路径,就只有主线程进行

2.    主函数只负责线程的开启,在主线程(任何一个线程中,代码是一步执行完才能执行下一步)

3.

关于创建线程的相关原理:

1.       为什么要继承Thread类:

在程序中我们首先只看得到主线程(主函数)可以明确主线程是由系统创建的,那么我们要思考Java是否为我们提供了一中创建线程的方式——通过查找API文档——找到继承Thread 这个方法

Thread 描述了线程事物所该具备的功能。

 

2.     为什么不直接用thread 创建对象:

直接用thread 创建的对象调用 start 方法,JVM调用的run 方法是没有任何执行的。不符合需求/在这个run 中没有我们需要执行的代码。

Run 中的代码就是这个线程要执行的部分,是这个线程的任务

创建线程的原因:是为了给一部分的代码建立单独执行的路径,实现多部分代码同时进行

3.    这部分关于内存:

        1.对象还是在堆内存中

        2.关于线程:

多线程时,在栈中会有分区(start 方法运行结束后),主线程区,其他线程区的名字自动生成:Thread-n(整数)

在创建线程对象时就会给线程自定义名字(整数是从0开始的),但是是否开启(对象是否调用start 方法才是决定这片栈空间是否存在)

在每个栈分区中代码的执行和内存的变化跟没有多线程时是一样的(方法进栈/弹栈),当线程中的方法全部出栈后,线程结束,相应的线程空间就会释放。线程与线程之间的运行是相互不干扰的。

【当栈中有多个线程时,CPU会在线程间随机的切换(执行是随机的/CPU在某一时刻是不可能同时执行多个程序的)】

————————————————————————————————————————————————————————————————————————————————————————————————————————

关于线程的名字:

自动生成  Thread-n(为整数)

返回正在执行的线程对象的引用:Thread.currentThread();

 

static Thread

currentThread()

返回对当前正在执行的线程对象的引用。 

获取引用的名字:

Thread.currentThread().getName();

在主函数中   new 出的对象是在堆内存中的,可以理解为是这个线程的所运行时要的数据,他被开启后就对应了一天路径/一个线程。获取线程对象引用获取的是路径的名字而不是堆中对象的名字(是实际运行的路径的名字)

————————————————————————————————————————————————————————————————————————————————————————————————————————

关于线程中的异常:

在异常学习中,JVM给出的异常提示:Exception in thread "main" java.lang.RuntimeException: 传入的年龄不合法!!

会给出异常所在的线程

————————————————————————————————————————————————————————————————————————————————————————————————————————

创建线程的方法二:

1.定义类实现接口:Runnable(只有一个方法Run )——避免单继承的局限性

2.实现Run 方法:将线程的任务写到Run  方法中,写出线程任务

3.创建实现类的对象

4.创建Thread 类的对象,将实现类的对象作为参数传入——只有创建Thread的对象才能创建线程

5.用Thread  的对象调用start  方法

package Thread;
class Demo1 implements Runnable{
    private String name;
    Demo1(String name){
        this.name=name;
    }
   public  void run(){
        for(int i=1;i<=20;i++){
            System.out.println("name : "+name+Thread.currentThread().getName()+"  "+i);
        }
    }
}
public class ThreadDemo2 {
    public static void main(String[] args){
        Demo1 D = new Demo1("LI");//不是线程对象
        Demo1 d = new Demo1("Liu");
        Thread T = new Thread(D);
        T.start();
        d.run();
    }
}

java什么时候要创建线程 什么时候需要创建线程_多线程_02

实现接口的原理:

1.实现接口Runnable 是为了避免单继承的局限性

2.为什么要将实现类的实例作为参数传入Thread类的实例

原因:(源码是王道)

package Thread;
class Demo1 implements Runnable{
    private String name;
    Demo1(String name){
        this.name=name;
    }
   public  void run(){
        for(int i=1;i<=20;i++){
            System.out.println("name : "+name+Thread.currentThread().getName()+"  "+i);
        }
    }
}
public class ThreadDemo2 {
    public static void main(String[] args){
        Demo1 D = new Demo1("LI");//不是线程对象
        Demo1 d = new Demo1("Liu");
        Thread T = new Thread(D);
        T.start();
        d.run();
    }
}

在Thread类的代码中,有参数为Runnable型的构造函数,在start方法中调用了 run   方法,run方法中有判断如果传入的Runnble  型的成员变量不为空则执行变量的Run方法

在Thread 类的源码中有这样的一段代码

class Thread{//实现了接口Runnable
    private Runnable target = null;
    Thread(Runnable tareget){
        this.target = tareget;
    }
    public void run(){
        if(target!=null){
            target.run();
        }
    }
    public void start(){
        run();
    }
}

Thread的实例才能创建线程,在线程创建时就要让线程知道自己的线程任务——始终明确线程开启的意义——执行代码/执行线程任务

自我总结:Thread或者其子类才能创建线程,开启线程任务的函数start  而start   函数里只有调用Run 方法这一个执行,但是Thread  本类的Run 方法只有一个判断语句,因此要写run  方法的内容,一:继承Thread  重写run 方法   二:实现Runnable  接口,实现 run 方法,再将实现类传入Thread  的实例

两种创建方法的使用区别:

继承Thread  :单独的一个类,没有继承谁

实现Runnable  :要被线程执行的类向上抽取有父类(继承了某个类)避免了单继承的局限性,更加的常用

更重要的一个好处:实现Runnable接口的方法更加的符合面向对象的思想,线程实际上分为两部分,线程对象和线程任务

继承Thread 是将线程对象耦合在一起——一旦创建Thread子类的对象,既有了线程对象又有了线程任务

实现Runnable 接口:将线程任务从线程对象中分离出来了单独的封装成了对象,类型为Runnable 。第二种方法是进行了解耦操作,每一部分分开封装成类更加能体现面向对象的思想。