Java多线程系列一(Java多线程的介绍与实现)

1.为什么要用多线程

之前一直对线程的概念很模糊,觉得特别抽象,后面看了许多的资料和书籍才知道,在这里假设一个场景,假如我们不用多线程,用一个线程就去把事给做了,举个栗子,当一个http请求过来,刚开始还好,我们去解析它的报文,但是在需要用字段作为条件去表里去查数据的时候,如果这张表里的数据特别多,那么该线程就需要去等待数据库系统返回值回来,这样就白白的浪费了时间。假如这个时候使用多线程,当前线程在等待数据库系统的返回值同时,另一个线程负责去渲染HTML页面,这样,当数据库返回值回来后,页面也可以直接渲染了。那么这就可以提高效率了。
另外也可以以物理电脑的例子来说明。现在为了提高的手机或者电脑设备的效率(本质上是为了解决处理器和内存存取之前的矛盾,处理器速度特别快,而内存存取速度慢),都是使用的多核处理器,每一个处理器都有对应的高速缓存,执行任务的过程中,因为涉及IO等耗时操作,十分影响性能,所以暂时将数据存储到存取速度快的高速缓存中,当执行完一个任务后将数据存到主存中。处理器,高速缓存关系如下。
处理器,高速缓存,主内存间的交互
Java内存模型1与物理机的模型相似。每个Java线程都有自己的工作内存(对应物理机中的高速缓存)。最后执行完任务后再存到主内存中的,在此过程中可能会有线程安全问题的出现,不过这待到后面的几篇文章详细解释。下图是线程,主内存,工作内存三者的交互关系图。
线程,主内存,工作内存三者的交互关系,与上面物理机模型对比

2.怎么实现多线程

我们解决了为什么,这个时候就需要解决怎么去实现多线程,共有四种方法
1)继承Thread类创建线程
2)实现Runnable接口创建线程
3)使用Callable和Future创建线程
4)使用线程池例如用Executor框架
具体怎么实现很简单,在这里先不说,在这里着重讲两点

Thread和runnable的区别

runnable本身只是一个接口,不能实现多线程,这是runnable的源码
在这里插入图片描述

runnable接口之所以可以实现多线程是因为Thread类的构造方法,Thread构造方法中可以传递一个runnable类型的参数进来。下图是Thread类的构造方法
在这里插入图片描述
使用runnable实现多线程用法
首先创建一个CycleWait,它是一个实现了runnable接口的类
在这里插入图片描述使用runnable实现多线程用法如下图,
使用

针对runnable和thread讲两点
第一.Thread类如果看源码其实是实现了Runnable 接口的类, 使得run 支持多线程。下图是Thread类里的方法,通过调用run方法,因为我们创建的类实现了runnable接口,因此依照依赖倒置原则,则会调用我们创建类的run方法

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

第二.因类的单一继承原则, 推荐多使用RunnabIe 接口

使用Callable方式实现多线程可以获得子线程的返回值

与继承Thread,实现runnable接口不同,实现callable实现多线程的方式可以获得线程的返回值,下面我们通过查看futureTask的源码的方式看它如何实现返回值。

FutureTask类的构造方法

下图是它的构造方法,构造方法的参数接收一个实现callable的类

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

FutureTask类的三个重要的方法

isDone方法用来判断该线程是否已经执行完成,我们先记住他

public boolean isDone() {
    return state != NEW;
}

get()方法用来,阻塞当前调用它的线程直到完成(如何阻塞的?看下面的if语句,如s表示状态,如果状态是在COMPLETING,这个单词翻译过来就是完成中,如果小于这个值说明未完成,因此调用awaitDone方法进行阻塞),如果完成就执行return语句,将返回值返回回去。

  /**
     * @throws CancellationException {@inheritDoc}
     */
    public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
        return report(s);
    }

跟上面的get()方法一样,但是加上了一个时间,如果超过了这个时间该线程还没结束,就抛时间超时异常。

 /**
     * @throws CancellationException {@inheritDoc}
     */
    public V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException {
        if (unit == null)
            throw new NullPointerException();
        int s = state;
        if (s <= COMPLETING &&
            (s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)
            throw new TimeoutException();
        return report(s);
    }

FutureTask接收返回值的用法

创建一个类,使用Callable方式实现多线程,在call方法里设置value值,准备返回给调用者。

在这里插入图片描述
创建一个FutureTaskDemo类,去获得上面MyCallable类中返回的值。
在这里插入图片描述
至于为什么可以像我图中标识的那样去创建线程。因为我们可以从Thread类中可以知道Thread的构造方法中并没有参数为FutureTask类型的构造方法,但是可以去FutureTask类的源码中查看可知它是实现了RunnableFutrure接口。而RunnableFutrure接口则继承了Runnable类。而Thread的构造方法中是有参数类型为Runnable类型的参数的。

public class FutureTask<V> implements RunnableFuture<V> {
public interface RunnableFuture<V> extends Runnable, Future<V> {
    /**
     * Sets this Future to the result of its computation
     * unless it has been cancelled.
     */
    void run();
}

这篇解决了为什么要用到多线程以及如何实现多线程,并在最后简单的讲了一下实现线程的返回值,也算是线程之间的通信的范畴。下篇详细的讲一下线程之间如何进行的。


  1. 这里说的Java内存模型和JVM内存结构是不一样的概念不要搞混淆了 ↩︎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值