多线程了解和构造方法使用

70 阅读7分钟

线程:线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。

简单理解:应用软件中互相独立,可以同时运行的功能

应用场景:

  • 软件的耗时操作
  •  拷贝,迁移文件
  • 加载大量的 资源文件

作用:提高效率,在等 待的时间中,去执行别的需求            

并发:在同一时刻,有多个指令在单个cpu上交替执行

          :比如你在打游戏时,手在莫键盘,又时不时吃东西

并行:在同一时刻,有多个指令在多个cpu上同时执行

           多个cpu的理解:是电脑的x核x线程,在计算机之中,随机的切换

多线程的实现:

1.自己定义一个类继承Thread

创建新执行线程有两种方法。一种方法是将类声明为 Thread 的子类。该子类应重写 Thread 类的run方法接下来以分配并启动该子类的实例

实现继承


//重写run方法,将要实现的代码写在run中

@Override

public void run() {

for (int i = 0; i <100 ; i++) {

System.out.println("hello word");

}

}

//在测试类中,实现多进程。

}

进行测试

public class Test\_1 {

public static void main(String\[] args) {

//创建继承子类的对象

code\_1 t1=new code\_1();

// ti.run() 不能直接调用方法,这样就是调用了一个构造方法了,不是多线程了

//开启线程

t1.start();

}

**}**

打印出来100结果。

转存失败,建议直接上传图片文件转存失败,建议直接上传图片文件

Image.png 可见,test测试类中,实现了多线程。这里我们在添加一个t2进程,来更直观看到多线程的运行


    //重写run方法,将要实现的代码写在run中

    @Override

    public void run() {

        for (int i = 0; i <100 ; i++) {

            System.out.println(getName()+"hello word");

        }

    }

    //在测试类中,实现多进程。

}

public class Test\_1 {

    public static void main(String\[] args) {

        //创建继承子类的对象

        code\_1 t1=new code\_1();

        code\_1 t2=new code\_1();

        //为两个线程起名字

        t1.setName("线程一");

        t2.setName("线程二");

       // ti.run()  不能直接调用方法,这样就是调用了一个构造方法了,不是多线程了

        //开启线程

        t1.start();

        t2.start();

    }

}

转存失败,建议直接上传图片文件转存失败,建议直接上传图片文件

Image.png 可以发现,控制台打印的结果中,线程一和线程二是随机出现的

2.实现Runable接口

创建线程的另一种方法是声明实现 Runnable 接口的类,该类然后实现 接口 法然后可以分配该类的实例,在创建  时作为个参数来传并启动。


    @Override

    public void run() {

        //书写要执行的代码

        for (int i = 0; i <100 ; i++) {

            //获取当前线程的对象

            Thread t=Thread.currentThread();

            //这里不能直接getname。因为Myrun这个类,和Runable是接口实现关系,不是Thread继承。

            //所以才有上面获取当前线程对象的的代码

            System.out.println(t.getName()+"hello");

        }

    }

}

public class Test {

    public static void main(String\[] args) {

        //创造实现类的对象

        //表达多线程要执行的任务

        Myrun mr=new Myrun();

        //创造线程对象

        Thread t1=new Thread(mr);

        Thread t2=new Thread(mr);

        //给线程设置名字

        t1.setName("线程一");

        t2.setName("线程二");

        //开启线程

        t1.start();

        t2.start();

    }

}

可以看到,实现Runnable接口,相比于Thread继承,仅仅多了一步”创造线程的对象“,还是十分方便的。

利用Callable接口和Future接口方式实现

特点: 可以获取到多线程运行的结果

  1. 创建一个类MyCallable实现Callable接口2。重写cal1 (是有返回值的,表示多线程运行的结果 )

  2. 创建MyCallable的对象 (表示多线程要执行的任务)

  3. 创建FutureTask的对象 (作用管理多线程运行的结果)

  4. 创建Thread类的对象,并启动 (表示线程)

创造三个变量分成:执行的任务 --多线程运行的结果---创造线程的对象

                             三个变量就可以调用该对应的方法来实现多线程

三种实现多线程的特点

总体分为两大类:可以获取结果(第三种),不能获取结果(前面两种)

Image.png

多线程中构造方法的使用

setname/getname:

为了方便,继承Thread类的demo省略此处

这里实现一个测试类


demo1 t2=new demo1();

//开启 线程

t1.start();   //Thread-0  这是他的setname/getname的默认名字

              //      -x  默认从0开始

t2.start();

t2.setName("线程二");

Thread.currentThread();//当前获取的对像

String name= t1.getName();

System.out.println(name);//main

我们执行上面这段代码时,给线程二使用了setName定义了名字。线程一并未进行定义。

程序执行发现,当我们获取线程一和线程二的对象时,线程一返回了“Thread-0”这个默认名字

线程二的名字依旧是我们定义的”线程二“。

除了set/getName的构造方法,我们话可以如下这么写

这里我们可以在类的构造方法中,直接为它取名

demo1 t3=new demo3(“线程一”)

但前提是,需要在子类中,用super使用父类的构造方法。才能够实现。

getProiority/setProiority 优先级:

  • 分为了十档,最小是一,最大是十。
  • 先来看看默认的执行规律:可以发现,优先级都是5
public class Test {

    public static void main(String\[] args) {

//创造线程要执行的参数对象

        demo mr=new demo();

        //创建线程对象

        Thread t1=new Thread(mr);

        Thread t2=new Thread(mr);

        System.out.println(t1.getPriority());//5

        System.out.println(t2.getPriority());//5

    }

}

然后我们设置一下线程的优先级

t1.setPriority(1);

t2.setPriority(10);

//启动线程

t1.start();

t2.start();
  • 将程序运行,我们会看到,线程二先执行完毕的概率总是大于线程一的。但并不是

  • 完全是线程二执行完毕先每次。

  • 所以,我们可以知道,线程的优先级别:是一个概率问题,并不是绝对谁先谁后

守护线程:setDaemon()

这里先创建两个Thread继续类。来观察守护线程的过程

转存失败,建议直接上传图片文件转存失败,建议直接上传图片文件

Image.png 测试类中执行一下

public class Test {

public static void main(String\[] args) {

//获取线程的任务对象

Thread1 t1=new Thread1();

Thread2 t2=new Thread2();

//名字

t1.setName("女神");

t2.setName("备胎");

//设置守护线程----将t2设置为守护线程

t2.setDaemon(true);

//启动线程

t1.start();

t2.start();

}

}

可以看到,当"女神"线程执行完了后,“备胎”线程,并没有执行完50次。而是慢慢的停止

所以得出守护线程的一个特点是:当非守护线程停止时,守护线程执行的内容,也会慢慢停止

转存失败,建议直接上传图片文件转存失败,建议直接上传图片文件

Image.png

这里可以设想一个场景:我们在qq和他人聊天时,常常会传输一些文件给别人。这时,我们可以把聊天当作为非守护线程

                                               发送文件当作守护线程。当电脑突然没电时,聊天自动结束了,此时文件的传输,它会缓缓的停下了

                                           最后退出。也就是等非守护线程执行完毕后,守护线程才会慢慢结束

插入线程和礼让线程 :


细节:表示可能的让出cpu线程执行权,和优先级有点相似。

             它会使得结果更加均匀

  


public static void join();插入线程/插队线程

t1.join();//表示把t1这个线程插入到当前线程之前

//当前线程:正在运行的线程