前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Thread 第三种创建方式

Thread 第三种创建方式

作者头像
PhoenixZheng
发布2018-08-07 16:50:56
3290
发布2018-08-07 16:50:56
举报
文章被收录于专栏:Phoenix的Android之旅

线程是面试时经常问到的问题,今天来说下线程的第三种创建方式。

说起线程,大家都熟悉如何新建并启动一个线程,立马想到的有这两种

代码语言:javascript
复制
new Thread().start()
代码语言:javascript
复制
new Thread(new Runnable(){
    @Override
    public void run(){
    ...
    }
}).start()

可以总结为第一种是直接new或者继承来重写Thread的run()方法来实现线程完成的任务,第二种则是通过实现了Runnable接口的类来实现线程操作。

还有第三种启动方式不知道大家了解么,就是FutureTask

前两种启动方式的问题

经过前两篇关于Thread的介绍,我们知道在Android里子线程是不允许直接操作主线程的,因此需要通过Handler来跟主线程交互 深入理解Android的Looper 这样一来前两种Thread的启动方式在代码上会显得不够灵活,因为我们需要在run()里面最后进行数据处理和分发。通常在多任务并发的场景我们就需要另外的方法了。

Future的作用

Future很好的解决了这个问题。Future是个接口,实现了这个接口的唯一类是FutureTask。 在多并发的场景中,通常会用线程池来管理Thread,而用FutureTask来包装具体的线程操作。 一个常见的代码可能如下

代码语言:javascript
复制
public class FeatureTaskSample {
    private static final String TAG = "FeatureTaskSample";

    private ExecutorService executorService = Executors.newFixedThreadPool(5);

    private List<FutureTask<String>> taskList = new ArrayList<FutureTask<String>>();

    public void executeTask() {
        for(int i = 0; i < 10; i++) {
            FutureTask<String> task = new FutureTask<String>(new CallableSample(i));
            taskList.add(task);
            executorService.submit(task);
        }

        for(FutureTask<String> task : taskList){
            try {
                Log.d(TAG, "executeTask: " + task.get());
            } catch (InterruptedException exp) {
                //do something
            } catch (ExecutionException exp) {
                //do something
            }
        }
    }


    class CallableSample implements Callable<String> {
        int num;

        public CallableSample(int i) {
            num = i;
        }

        @Override
        public String call() throws Exception {
            Thread.sleep(1000);
            String result = String.valueOf(this.num);
            String currTime = String.valueOf(System.currentTimeMillis());
            return result + "_" + currTime;
        }
    }
}

FutureTask同时实现了Runnable和Future接口,而Executor可以接受实现了Runnable接口的对象,因此可以把FutureTask提交给线程池去执行。 FutureTask的run()接口在被线程调用后,会去调用构造时传入的Callable对象,在我们的代码里就是CallableSample的call()方法。 Future的get()接口呢,其实是个阻塞方法,它会在等到call()方法结束后才会返回。因此如果在Android的主线程中调用,会造成线程阻塞。对于这种情况,下面的代码是更好的解决办法

代码语言:javascript
复制
public class SampleFutureTask {

    private static final String TAG = "SampleFutureTask";

    private ExecutorService executorService = Executors.newFixedThreadPool(5);

    private List<FutureTask<String>> taskList = new ArrayList<FutureTask<String>>();

    public void executeTask() {
        for(int i = 0; i < 10; i++) {
            FutureTask<String> task = new FutureSample(new CallableSample(i));
            taskList.add(task);
            executorService.submit(task);
        }
    }

    class FutureSample extends FutureTask<String> {
        Callable<String> callable ;
        public FutureSample(@NonNull Callable<String> callable) {
            super(callable);
            this.callable = callable;
        }

        @Override
        protected void done() {
            try {
                Log.d(TAG, "executeTask: " + this.get());
            } catch (ExecutionException e) {

            } catch (InterruptedException exp) {

            }
        }
    }

    class CallableSample implements Callable<String> {
        int num;

        public CallableSample(int i) {
            num = i;
        }

        @Override
        public String call() throws Exception {
            Thread.sleep(1000);
            String result = String.valueOf(this.num);
            String currTime = String.valueOf(System.currentTimeMillis());
            return result + "_" + currTime;
        }
    }
}

这里面构造了一个内部类重载了FutureTask的done()方法,这个方法在call()结束后会调用,这样就不用等我们主动去调用get()方法并等待线程返回了。而Android中的AyncTask用的也是这种思路,AsyncTask多做的部分是在get()到数据后,通过Handler把数据丢还到主线程,并在onPostExecute()中去处理。

下面是这段代码的输出结果

D/SampleFutureTask: executeTask: 0_1523847353651 D/SampleFutureTask: executeTask: 2_1523847353651 D/SampleFutureTask: executeTask: 1_1523847353652 D/SampleFutureTask: executeTask: 3_1523847353652 D/SampleFutureTask: executeTask: 4_1523847353653 D/SampleFutureTask: executeTask: 5_1523847354651 D/SampleFutureTask: executeTask: 6_1523847354652 D/SampleFutureTask: executeTask: 7_1523847354652 D/SampleFutureTask: executeTask: 8_1523847354653 D/SampleFutureTask: executeTask: 9_1523847354654

总结

采用实现Runnable、Callable接口的方式创见多线程时 · 优势是: 线程类只是实现了Runnable接口或Callable接口,还可以继承其他类。 在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。 · 劣势是: 编程稍微复杂,如果要访问当前线程,则必须使用Thread.currentThread()方法。

使用继承Thread类的方式创建多线程时 · 优势是: 编写简单,如果需要访问当前线程,则无需使用Thread.currentThread()方法,直接使用this即可获得当前线程。 · 劣势是: 线程类已经继承了Thread类,所以不能再继承其他父类。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-03-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Android每日一讲 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前两种启动方式的问题
  • Future的作用
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档