面对Java多线程,还是一首凉凉送给自己?

Java多线程,这个名字听起来应该还是比较害怕的,有多少人在这块被坑过。那坑起来,真的是一各萝卜一个坑,一筐罗卜满地坑。给你举一个形象的例子,这就好比你在盖房子,房子快封顶了,感觉也没啥毛病,然后就在你开行的封顶的时候,墙塌了。就是这种感受,很难受。你要是还不能体会到这种感受,就在给你举一个,好比你们连个人去酒店(至于干啥,咱也不能问),然后你感觉自己没问题,就在你跃跃欲试的时候,就在你厉兵秣马的时候,感觉就像一座火山即将喷发的时候,突然,一盆冷水过来,告诉你,兄弟,你,不行。这是什么感觉。这就是你用线程的感觉。

自己技术本来就菜,Java的理解也不多。单单遇到很苦恼的事情。事情是这样的:

案件回放

由于需要,我要在 Runnable 里面拿到一个值返回。举个例子:

class MyRunnable implements Runnable{
    int id = 0 ;
    @Override
    public void run() {
        id = 1 ;
    }
}

不确定这个例子准不准确。现在我去实例化这个 MyRunnable , 我现在要拿到这里面的 id , 但是呢,你怎么获取到呢?在外面获取的话,你拿到的就有可能是 0 , 人家还没赋值呢,这里面又 return 不了,就很愁人。

解决

在真实的环境中是拿不到这个被重新赋值后的 id 值的。那问题来了,我一定要拿到,那我可以等这个 run 执行完去拿,那我怎么知道它啥时候执行完?只要时间不长,还是可以等的吗。而且呢,这个 Runnable 特别讨厌。我们看这个Runnable,她其实是一个函数式接口。从JDK1.0就跟着了,这还是元老级别的。难怪这么难用。然后呢,我们知道,Thread 是这个Runnable 的一个实现类。所以我们写线程,一般会用Thread,或者是Runnable。

笔者在网上挣扎了好久,看到有一个接口和Runnable很相似(以前学Java的时候,有看这块,但是时间长,忘了),叫 Callable 。大家看这个源码:

@FunctionalInterface
public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}

看到没有,他可以返回这个值,也就是说,我们只要去实现这个接口,他返回的就可以是我们的计算结果,是不是。现在问题又来了。我们的线程他就认Runnable,咋办?不怕,我们还有东西:FutureTask<V>这个源码呢,我们先看一个关系图:
在这里插入图片描述

先不看别的,先看这里面的两个构造器:

    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }

    public FutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       // ensure visibility of callable
    }

再看他实现的接口:

implements RunnableFuture<V>

public interface RunnableFuture<V> extends Runnable, Future<V> 

现在,这个FutureTask 就相当于一个 Runnable 了。可以加入到我们的 Thread 里面去了。我们再看这个 FutureTask 里面的 run 方法:

    public void run() {
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);
            }
        } finally {
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }

可以看到,最后是把结果 set 了一下。这个结果也就是 call 的返回结果,这样的话,我们不就相当于是等线程执行完就拿到结果了吗。这个时间还是可以等的。

步骤如下:

  • 实现一个 Callable 的类,重写里面的 call 方法;
  • 将实现类的实例加入到 FutureTask 里面去;
  • 把 FutureTask 实例加入到线程 Thread里面去。
  • 获取值。使用 future.get() , 如果线程没执行完,这个将会被阻塞住。

总结

多线程的使用其实很复杂的,基本可以写成一本书,我们要做的是将自己遇到的每一个问题记录下来,再分享出去。这是博客的意义所在。

发布了100 篇原创文章 · 获赞 32 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_33356083/article/details/105276186
今日推荐