美文网首页
多线程之(线程创建)

多线程之(线程创建)

作者: 新征程Dream | 来源:发表于2020-06-28 23:25 被阅读0次

    [Toc]

    线程创建

    作为一名优秀的程序员,怎么能不懂多线程呢?那想搞懂多线程,第一步就是先创建线程跑起来。说到创建线程,方式多种多样。其实最根本的就是三种方式,剩下的无非就是语法变一变的玩意。

    创建线程的三种方式

    • 继承Thread
    • 实现Runnable接口
    • 实现Callable接口

    第一种,继承Thread.

    先说为什么要继承Thread类呢?为什么不直接new 一个Thread对象来创建线程呢?原则上是可以直接new 一个Thread对象来创建一个线程。但是我们要想一下创建一个线程做什么呢?对的,肯定是想来跑我们自己的代码。我们应该知道当线程被开启执行的是run()方法。即使不知道,那就记住。这个run()方法就存在于Thread中。但是呢Thread中的run()方法几乎是没什么东西的,那我们肯定要把自己的代码写到run()方法中,那怎么来写呢?继承父类就可以重写。这样讲,就明白为什么要继承Thread类了吧。
    话不多说上码

    package cn.zl;
    // 第一种方式创建线程,通过继承Thread。
    public class ThreadTest extends Thread{
        private int count = 10000;
        @Override
        public void run() {
            while (true){
                System.out.println("count"+count);
                if (--count<0){
                    break;
                }
            }
        }
    }
    

    既然我们的线程已经创建好,那就跑一把。

    package cn.zl;
    public class Main {
        public static void main(String[] args) {
          // 创建线程
            ThreadTest threadTest = new ThreadTest();
          //  启动线程
            threadTest.start();
          // 为了我们执行的结果可以交叉显示,所以写了以下代码。
            int i=10000;
            while (i>0){
                System.out.println("Main"+i);
                i--;
            }
        }
    }
    

    这时候,有个重点,就是我们线程启动,是通过start()方法,而不是直接调用run()方法。想要搞定这个东西,都需要研究一下JVM。因为start()方法其实是一个本地方法,由C/C++来实现的。但是对于我们Java程序员来讲,我们要记住线程启动时通过调用start()方法。第一种方式已经完成,学习是一个循序渐进的过程,尤其对java程序员来说,先学会怎么用,再去研究原理。

    第二种,实现Runnable接口.

    还是刚才那个问题,有没有不通过继承Thread的方式来重写run()方法呢?答案肯定是有的。Thread这个类对外提供了传参Runnable的实现类来重写run()方式。于是这就是我们要说的第二种了。通过实现Runnable接口中的run()方法,并把此实现类当做参数传入Thread中。
    话不多说,上码

    package cn.zl;
    // 第二种线程创建的方式,实现Runnable
    public class RunnableTest implements Runnable {
        private int i = 10000;
        public void run() {
            while (i > 0) {
                System.out.println("Runnable" + i);
                i--;
            }
        }
    }
    

    跑一把

    package cn.zl;
    public class Main {
        public static void main(String[] args) {
            // 创建一个线程
            Thread thread = new Thread(new RunnableTest());
            // 启动线程
            thread.start();
           // 为了我们执行的结果可以交叉显示,所以写了以下代码。
            int i=10000;
            while (i>0){
                System.out.println("Main"+i);
                i--;
            }
        }
    }
    

    这样第二种方式就完成了。稍后讲一下三种的区别。既然存在三种,三种一定有所不同。

    第三种,实现Callable接口

    这一种,猛一下类似于第二种。这一种,我们不是通过实现run()方法来重写Thread中的run()方法,而是我们通过实现call()方法来重写Thread中的run()方法。
    废话不多说,上码

    package cn.zl;
    import java.util.concurrent.Callable;
    // 第三种创建线程的方式,实现Callable方式
    public class CallableTest implements Callable<Integer> {
        private int i = 10000;
        public Integer call() throws Exception {
            while (i>0){
                System.out.println("Callable"+i);
                i--;
            }
            return i;
        }
    }
    

    跑一下

    package cn.zl;
    
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.FutureTask;
    
    public class Main {
        public static void main(String[] args) {
             // 创建Future对象来装载Callable的实现类对象(CallableTest)。
            FutureTask<Integer> integerFutureTask = new FutureTask<Integer>(new CallableTest());
            // 创建线程
            Thread thread1 = new Thread(integerFutureTask);
           // 启动线程
            thread1.start();
            try {
              //  获取线程中的返回值,并输出
                System.out.println(integerFutureTask.get()+"--------------");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
            // 为了我们执行的结果可以交叉显示,所以写了以下代码。
            int i=10000;
            while (i>0){
                System.out.println("Main"+i);
                i--;
            }
        }
    }
    

    通过以上的代码,我们可以感受到。用实现Callable方式,我们不仅能创建线程,还能获取线程的返回值。而不是像之前一样,线程启动了,到结束都不给我们打个招呼。这种方式,就比较友好。不受控,对程序员来说是一件很可怕的事情。

    三种方式的优缺点

    • 采用继承Thread类方式:
      (1)优点:编写简单,如果需要访问当前线程,无需使用Thread.currentThread()方法,直接使用this,即可获得当前线程。当然是在我们自己写的子类中使用this。
      (2)缺点:因为线程类已经继承了Thread类,所有不能再继承其他的父类,这是Java语言的一大特性,单继承多实现。
    • 采用实现Runable接口方式:
      (1)优点:线程只是实现了Runnable接口,还可以继承其他的类。在这种方式下,可以多个线程共享同一目标对象,所有非常适合多个相同的线程处理同一份资源的情况,从而可以将CPU代码和数据分开,形成清晰的模型。
      (2)缺点:编程稍微复杂,如果需要访问当前线程,必须使用Thread.currentThread()方法。
    • Runnable 和Callable的区别:
      (1)Callable 规定的方法是call(),Runable规定的是run();
      (2)Callable 的任务执行后可返回值,而Runable的任务是不能获取返回值的。
      (3)call 方法可以抛出异常,run方法不可以,因为run方法本身没有抛出异常,所以自定义的线程类在重写run的时候也无法抛出异常。
      (4) 运行Callable 任务可以拿到一个Future对象,表示异步计算的接口。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果。

    start()和run() 的区别

    • start()方法用来,开启线程,但是线程开启后并没有立即执行,他需要获取CPU的执行权才可以执行。
    • run()方法有JVM 创建完本地操作系统级线程后回调的方法,不可以手动调用(否则就是普通方法)。

    消化一下,后续写更深点的东西,学习讲究循序渐进。

    相关文章

      网友评论

          本文标题:多线程之(线程创建)

          本文链接:https://www.haomeiwen.com/subject/mnigfktx.html