【Java】多线程基础(一)


多线程是 Java相关开发者不可跳过的环节,服务端,客户端都会遇到,所以今天借着机会,巩固一下多线程相关的知识。前篇主要讲述多线程的基础知识,从创建和启动入手,然后再介绍线程的状态,下篇主要讲解线程间的操作同步等

1. 线程创建和启动

线程的创建可以有多种方式,常见的为继承 Thread 和 实现 Runnable 借口,当然 也有直接通过new Thread()方式创建线程。这里我们主要掌握Thread以及Runable方式

1.1 继承 Thread 类

先上代码

//MyThread.java
public class MyThread extends Thread{
    @Override
    public void run() {
        doSomething() //需要执行的操作
    }
}

// Main.java
public class Mian{
  public static void main(String[] args){
    MyThread thread = new MyThread(); //实例化线程
    thread.start() //创建和启动线程
  }
}

通过上述代码可以看到,我们需要覆写run方法,在这个方法里面添加我们需要执行的操作,然后创建线程的时候只需要将MyThread实例化,最后通过start()方法启动线程即可。

1.2 实现 Runnable接口

在讲通过 Runnable 之前,我们先看一下 Thread 源码,如下图:

Thread

通过代码我们可以发现,其实 Thread 也是实现了 Runable 接口,相当于是 JDK 已经帮我们封装好了一个启动线程的类,所以接下来就很简单了,直接上代码

// MyRunnable.java
class MyRunnable implements Runnable{
    @Override
    public void run() {
        doSomething(); //需要执行的操作
    }
}

// Main.java
public class Mian{
  public static void main(String[] args){
    MyRunnable runnable = new MyRunable();
    Thread thread = new Thread(runnable); //实例化线程
    thread.start(); //创建和启动线程
  }
}

通过代码我们发现,其实 Runnable 接口的方式和Thread 方式基本一样,最终还是通过实例化 Thread 方式,然后调用 Threadstart()方法创建和启动线程的。

拓展

  1. 如何获取当前的线程名称?

    通过 Thread.*currentThread*().getName()即可获取当前线程名称

  2. 细心的同学可能已经注意到 new Thread 这里的代码,我注释的是实例化线程,而非创建线程。因为通过源码我们可以发现,new Thread 的时候,只是进行一些属性的赋值,而真正创建和启动线程是通过 start()方法,调用了 native 的 start0()这个方法实现。这一点我们从源码的注释可以确认,调用 start()方法会有两个线程返回值,一个是 start()的返回,一个是 run() 方法的返回.

创建线程

  1. 上面代码分析我们得知 Thread 内部的 run 方法是在另外一个线程中执行的,如果我们直接在主线程中调用会是什么结果呢?

    答案就是,如果在主线程中调用,这个方法就是个普通方法,也就是 Thread 这个实例的方法,还是执行在主线程中。

1.3 使用 Callable 和 FutureTask

以上两种方式,都是通过 Runnable 的 run()方法执行,因为 run 方法是 没有返回值的,如果想要在子线程中获取返回值,则可以通过 Callable + FutureTask方式

首先看 Callable 接口代码:

public interface Callable<V> {
    V call() throws Exception;
}

可以看到和 Runnable 不同的是,call 方法是有返回值的且可以抛出异常,另外 Callable 借口也是有一个泛型参数,call 的返回值就是泛型指定的类型。

那么如何通过 Callable+Future 方式创建线程呢?我们看下代码:

// MyCallable.java
class MyCallable implements Callable<Object>{
    @Override
    public Object call() throws Exception {
        doSomething();
        return result;
    }
}


// Main.java
public class Mian{
    public static void main(String[] args){
        MyCallable callable = new MyCallable(); //实例化 Callable
        FutureTask task = new FutureTask<>(callable); // 创建 future task
        Thread thread = new Thread(task); // 实例化线程
        thread.start(); //创建和启动线程
        try{
            task.g et(); // 获取子线程返回结果
        }catch(Exception e){
            doSomething();
        }
    }
}

以上就是通过 Callable 方式创建线程。可以看出相比 Runnable 方式,Callable 方式更为复杂

拓展

首先 FutureTask 这个类,实现了 RunnableFuture 接口,RunnableFuture 是继承了 Runnable 以及 Future接口。所以其本身就是一个 Runnable 的实现。这也就解释了为什么可以通过 new Thread方式来完成线程的创建和启动。

从上面我们知道 Thread 最终会在线程中调用 run 方法,所以我们来看一下 run 方法:

    // FutureTask#run()
    public void run() {
        if (state != NEW ||
            !RUNNER.compareAndSet(this, null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    result = c.call();   //执行 call 方法
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);  // set 到当前对象中
            }
        } 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);
        }
    }

从上面代码可以看出 首先通过 执行Callable 对象的 call() 方法获取返回的结果,然后讲结果通过 set(result) 方法设置到 FutureTask对象中。所以这也解释了为什么最后可以通过 get()方法拿到返回值

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

0neXiao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值