倒霉的菜鸟

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

简单理解启动线程的几种方式:

1. 实现Runnable接口创建一个任务, 调用myTask.run()方法来启动它

 

 

 

 2, 构建Thread对象, 调用thread.start()方法启动

这里可以看到, 我们给Thread构造器传入了runable对象

 

细心的小伙伴看到这里可能会有疑惑, 我们知道Thread同样有run方法, 这里为什么要调用thread.start()而不是thread.run()呢 ?

 

 这里的target就是我们传进去的runable对象,所以thread的run方法里也是执行了runable的run方法 

再看下start方法, 我这里看到的是android-30的源码,可以看到Android在这里做了一些修改

注释里这段英文的意思是说:

调用该方法来启动一个线程时, java VM会去call run方法

调用该方法后会有两个线程同时运行(主线程执行完 start()后返回,调用run方法后子线程也开始执行)

多次启动一个线程是非法的

特别是, 如果一个线程被执行完成, 它将不能再被启动

 

 

 所以, 回答上面那个问题, 为什么调用start来启动线程而不是直接调用run呢?

因为start方法中 1), 会判断当前线程的状态,如果已经被启动, 则会抛出 IllegalThreadStateException

                      2), 会将该线程加到 一个group里, 这个group可能包含了当前线程的多个实例。加到这里有什么好处呢?。。。。,,等我知道了再来写

 

3, 通过Executor来管理线程

CachedThreadPool

 

 FixedThreadPool创建固定数量的线程池

 

 

SingleThreadExecutor

 这里我们创建了3个线程(id分别为0,1,2),并使用singleThreadExecutor来启动它, 看下运行结果

 

 我们看到: 1), 线程是严格按照我们期望的顺序去启动的(0--> 1 --> 2)。 我们知道在多线程中, 线程间的调度是由线程调度器来完成的, 调度过程可以说是不确定的, 但是SingleThreadExecutor会按照线程被提交的顺序去执行。所以我们说SingleThreadExecutor会序列化所有提交给它的任务,并且维护它自己的悬挂任务队列。

                 2), 第一个线程执行完成之后才去执行下一个

因此,如果我们有几个线程需要访问同一个系统资源时, 使用这种方式可以确保在任何时刻都只有唯一的线程在操作数据, 从而实现同步,而且线程执行的顺序还是可控的。

 

4, 通过Callable接口创建线程

在上文中, 我们尝试了通过实现Runnable接口来创建线程。 但是它并不会返回任何值。

那么如果我们希望在任务完成时有返回值, 就可以实现Callable接口。 实现callable接口需要重写call方法, call方法的返回值就是该任务的返回

需要特别注意的是, 我们必须使用ExecutorService.submit()方法来启动它

 

 

 

 看下运行结果:

 5, 继承Thread类创建线程

 

 

 

 

 

小结--创建/启动线程的方式:

 

1)让我们的task实现runnable接口, 重写run方法, 使用mytask.run()启动

2)让我们的task实现runnable接口,重写run方法, 使用ExecutorService.execute(runnable)方式启动。 常用的ExecutorService有 CachedThreadPool, FixedThreadPool 和 SingleThreadExecutor

3)在Thread的构造方法中传入runnable对象, 使用thread.start()方式启动 (start方法本质上还是去调用了run方法, 但是在调用之前多了一些检查的工作)

4)让我们的task继承自Thread, 重写run()方法, 使用thread.start()方式启动

5)让我们的task实现Callable接口, 使用ExecutorService.submit()方式启动。 该方法可以在任务执行完后给我们返回值

其中1,2,3可以归为同类, 因为都是实现了runnable接口

 

posted on 2021-09-22 18:47  倒霉的菜鸟  阅读(1019)  评论(0编辑  收藏  举报