线程的创建

Posted wuqinglong

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线程的创建相关的知识,希望对你有一定的参考价值。

目录

概述

多线程能大幅度的提升CPU的使用率, 使得任务处理更加快速, 但是也并不是多线程一定高效, 线程的切换会涉及到CPU上下文切换, 上下文的切换会大幅度的拉低CPU的性能.

线程的五种状态

新建状态(New)

当线程对象被创建后, 即进入了新建状态. 如: Thread thread = new MyThread();

就绪状态(Runnable)

当调用线程对象的start()方法后, 线程随即进入了就绪状态. 处于就绪状态的线程, 只能说明此线程已经做好了准备, 随时等待CPU调度执行, 并不是调用了start()之后就开始执行了.

运行状态(Running)

当CPU调度处于就绪状态的线程时, 线程才得以真正的执行, 随即进入了运行状态. 就绪状态是进入运行状态的唯一入口, 线程要想进入运行状态, 必须处于就绪状态.

阻塞状态(Blocked)

处于运行状态的线程由于某种原因, 暂时放弃对CPU的使用权, 停止执行, 此时进入阻塞状态. 直到其进入就绪状态, 才有机会被CPU重新调度.

进入阻塞状态可能的原因:

  1. 线程调用sleep()方法, 使线程进入休眠状态.
  2. 线程调用wait()方法使线程挂起, 直到被notify()或notifyAll()通知.
  3. 线程等待IO的完成.
  4. 线程在等待同步锁的释放.

死亡状态(Dead)

线程执行完成后或者因为异常退出了run()方法, 则线程的生命周期结束了.

线程状态的切换

箭头(->)表示可以切换到后续状态

  • 新建状态 -> 就绪状态
  • 就绪状态 -> 运行状态
  • 运行状态 -> 就绪状态/阻塞状态/死亡状态
  • 阻塞状态 -> 就绪状态
  • 死亡状态 ->

线程的创建

继承Thread类

通过继承Thread类, 重写run()方法可以实现线程的创建.

线程执行体就是run()方法中的内容.

public class MyThread extends Thread {

    @Override
    public void run() {

        // do something...

    }
}
public class Demo {

    public static void main(String[] args) {

        new MyThread().start();

    }

}

创建MyThread对象, 然后调用start()方法即可启动线程. 虽然线程的执行体是run()方法中的内容, 但是不能直接使用new MyThread().run();启动线程. 这种方式启动的话就是普通方法的调用, 并不会有线程的创建.

实现Runnable接口

通过实现接口, 并实现接口中的run()方法, 然后在创建Thread对象时作为参数传入即可实现线程de创建. 线程执行体也就是run()方法中的内容.

public class MyThread implements Runnable {

    @Override
    public void run() {

        // do something...

    }
}
public class Demo {

    public static void main(String[] args) {

        new Thread(new MyThread()).start();

    }

}

区别

两种创建方法的区别就是一个继承类, 一个是实现接口, 根据Java的语法规范可知, 继承是单继承而实现可以多实现.

打开Thread的源码可知, Thread实现了Runnable接口, 继续打开Thread的run()看到

@Override
public void run() {
    if (target != null) {
        target.run();
    }
}

这里的target即为第二种方式传入的new MyThread(), 如果直接使用new Thread().start();, 结果就是创建一个线程, 但是线程没有线程执行体(没有执行代码), 随即结束线程. 因为target为null.

使用Callable创建线程

这种方式与使用Runnable创建类似, 但是该方式更加强大, 主要体现在两方面: 1. Callable可以抛出异常; 2. Callable可以有返回值.

并且, Callable在获取返回值的时候(调用get()方法时)会等待线程执行完成.(肯定要等待线程执行完成才能拿到返回值.)

使用:

public class MyCallable implements Callable<Integer> {

    @Override
    public Integer call() throws Exception {

        // do something...

        return 10;
    }
}
public class Demo {

    public static void main(String[] args) throws Exception {

        // 实例化对象
        MyCallable callable = new MyCallable();

        // 使用FutureTask封装
        FutureTask<Integer> task = new FutureTask<>(callable);

        // 启动线程
        new Thread(task).start();

        // 获取返回值, get()方法会有异常抛出
        Integer integer = task.get();

    }

}

如果这时你对Thread对象也可以接受FutureTask对象有疑问的话? 那么你非常棒.

打开FutureTask的源码看到他实现了RunnableFuture接口, 而RunnableFuture接口又继承了Runnable接口, 所以Thread可以接受FutureTask对象.

这时你如果能想到既然FutureTask实现了Runnable接口, 那么它的run()是如何实现的有疑问的话? 你的求知欲很强. 看看源码就知道了, 也会知道为什么需要重写call().

我们本应该就带有很强的求知欲, 去探索底层实现. 不然和机器有什么区别呢.

以上是关于线程的创建的主要内容,如果未能解决你的问题,请参考以下文章

多个请求是多线程吗

片段android的缓慢创建

线程同步-使用ReaderWriterLockSlim类

线程池与并行度

活动到片段方法调用带有进度条的线程

如何启动匿名线程类