关于程序员:创建-Java-多线程有哪几种方式

3次阅读

共计 5584 个字符,预计需要花费 14 分钟才能阅读完成。

本文首发自「慕课网」,想理解更多 IT 干货内容,程序员圈内热闻,欢送关注!

作者 | 慕课网精英讲师 ColorfulCJava

多线程本篇文章咱们介绍一下如何创立线程,创立线程有哪几种形式,线程的状态、生命周期等内容。1. 什么是线程要理解什么是线程,就要先理解过程的概念。过程,是指计算机中已运行的程序,它是一个动静执行的过程。假如咱们电脑上同时运行了浏览器、QQ 以及代码编辑器三个软件,这三个软件之所以同时运行,就是过程所起的作用。线程是操作系统可能进行运算调度的最小单位。大部分状况下,它被蕴含在过程之中,是过程中的理论运作单位。也就是说一个过程能够蕴含多个线程,因而线程也被称为轻量级过程。如果你还是对于过程和线程的概念有所困惑,举荐一篇比拟优良的文章,有助于帮忙你了解过程和线程的概念。2. 创立线程在 Java 中,创立线程有以下 3 种形式:继承 Thread 类,重写 run() 办法,该办法代表线程要执行的工作;实现 Runnable 接口,实现 run() 办法,该办法代表线程要执行的工作;实现 Callable 接口,实现 call() 办法,call() 办法作为线程的执行体,具备返回值,并且能够对异样进行申明和抛出。上面咱们别离来看下这 3 种办法的具体实现。2.1 Thread 类 Thread 类是一个线程类,位于 java.lang 包下。2.1.1 构造方法 Thread 类的罕用构造方法如下:Thread():创立一个线程对象;Thread(String name):创立一个指定名称的线程对象;Thread(Runnable target):创立一个基于 Runnable 接口实现类的线程对象;Thread(Runnable target, String name):创立一个基于 Runnable 接口实现类,并具备指定名称的线程对象。2.1.2 罕用办法 void run():线程相干的代码写在该办法中,个别须要重写;void start():启动以后线程;static void sleep(long m):使以后线程休眠 m 毫秒;void join():优先执行调用 join() 办法的线程。Tips:run() 办法是一个十分重要的办法,它是用于编写线程执行体的办法,不同线程之间的一个最次要区别就是 run() 办法中的代码是不同的。可翻阅官网文档以查看更多 API。2.1.3 实例通过继承 Thread 类创立线程可分为以下 3 步:定义 Thread 类的子类,并重写该类的 run() 办法。run() 办法的办法体就代表了线程要实现的工作;创立 Thread 子类的实例,即创立线程对象;调用线程对象的 start 办法来启动该线程。具体实例如下:/**

  • @author colorful@TaleLin
    */

public class ThreadDemo1 extends Thread {

/**
 * 重写 Thread() 的办法
 */
@Override
public void run() {System.out.println("这里是线程体");
    // 以后打印线程的名称
    System.out.println(getName());
}

public static void main(String[] args) {
    // 实例化 ThreadDemo1 对象
    ThreadDemo1 threadDemo1 = new ThreadDemo1();
    // 调用 start() 办法,以启动线程
    threadDemo1.start();}

}
代码块 1234567891011121314151617181920212223 运行后果:这里是线程体
Thread-0
代码块 12 小伙伴们可能会有疑难,下面这样的代码,和一般的类实例化以及办法调用有什么区别的,上面咱们来看一个略微简单些的实例:/**

  • @author colorful@TaleLin
    */

public class ThreadDemo2 {

/**
 * 动态外部类
 */
static class MyThread extends Thread {

    private int i = 3;

    MyThread(String name) {super(name);
    }

    @Override
    public void run() {while (i > 0) {System.out.println(getName() + "i =" + i);
            i--;
        }
    }

}

public static void main(String[] args) {
    // 创立两个线程对象
    MyThread thread1 = new MyThread("线程 1");
    MyThread thread2 = new MyThread("线程 2");
    // 启动线程
    thread1.start();
    thread2.start();}

}
代码块 123456789101112131415161718192021222324252627282930313233343536 运行后果:线程 2 i = 3
线程 1 i = 3
线程 1 i = 2
线程 2 i = 2
线程 1 i = 1
线程 2 i = 1
代码块 123456 代码中咱们是先启动了线程 1,再启动了线程 2 的,察看运行后果,线程并不是依照咱们所料想的程序执行的。这里就要划重点了,不同线程,执行程序是随机的。如果你再执行几次代码,能够察看到每次的运行后果都可能不同:

2.2 Runnable 接口 2.2.1 为什么须要 Runnable 接口通过实现 Runnable 接口的计划来创立线程,要优于继承 Thread 类的计划,次要有以下起因:Java 不反对多继承,所有的类都只容许继承一个父类,但能够实现多个接口。如果继承了 Thread 类就无奈继承其它类,这不利于扩大;继承 Thread 类通常只重写 run() 办法,其余办法个别不会重写。继承整个 Thread 类老本过高,开销过大。2.2.2 实例通过实现 Runnable 接口创立线程的步骤如下:定义 Runnable 接口的实现类,并实现该接口的 run() 办法。这个 run() 办法的办法体同样是该线程的线程执行体;创立 Runnable 实现类的实例,并以此实例作为 Thread 的 target 来创立 Thread 对象,该 Thread 对象才是真正的线程对象;调用线程对象的 start 办法来启动该线程。具体实例如下:/**

  • @author colorful@TaleLin
    */

public class RunnableDemo1 implements Runnable {

private int i = 5;

@Override
public void run() {while (i > 0) {System.out.println(Thread.currentThread().getName() + "i =" + i);
        i--;
    }
}

public static void main(String[] args) {
    // 创立两个实现 Runnable 实现类的实例
    RunnableDemo1 runnableDemo1 = new RunnableDemo1();
    RunnableDemo1 runnableDemo2 = new RunnableDemo1();
    // 创立两个线程对象
    Thread thread1 = new Thread(runnableDemo1, "线程 1");
    Thread thread2 = new Thread(runnableDemo2, "线程 2");
    // 启动线程
    thread1.start();
    thread2.start();}

}
代码块 12345678910111213141516171819202122232425262728 运行后果:线程 1 i = 5
线程 1 i = 4
线程 1 i = 3
线程 1 i = 2
线程 2 i = 5
线程 1 i = 1
线程 2 i = 4
线程 2 i = 3
线程 2 i = 2
线程 2 i = 1
代码块 123456789102.3 Callable 接口 2.3.1 为什么须要 Callable 接口继承 Thread 类和实现 Runnable 接口这两种创立线程的形式都没有返回值。所以,线程执行结束后,无奈失去执行后果。为了解决这个问题,Java 5 后,提供了 Callable 接口和 Future 接口,通过它们,能够在线程执行完结后,返回执行后果。2.3.2 实例通过实现 Callable 接口创立线程步骤如下:创立 Callable 接口的实现类,并实现 call() 办法。这个 call() 办法将作为线程执行体,并且有返回值;创立 Callable 实现类的实例,应用 FutureTask 类来包装 Callable 对象,这个 FutureTask 对象封装了该 Callable 对象的 call() 办法的返回值;应用 FutureTask 对象作为 Thread 对象的 target 创立并启动新线程;调用 FutureTask 对象的 get() 办法来取得线程执行完结后的返回值。具体实例如下:import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**

  • @author colorful@TaleLin
    */

public class CallableDemo1 {

static class MyThread implements Callable<String> {

    @Override
    public String call() { // 办法返回值类型是一个泛型,在下面 Callable<String> 处定义
        return "我是线程中返回的字符串";
    }

}

public static void main(String[] args) throws ExecutionException, InterruptedException {
    // 常见实现类的实例
    Callable<String> callable = new MyThread();
    // 应用 FutureTask 类来包装 Callable 对象
    FutureTask<String> futureTask = new FutureTask<>(callable);
    // 创立 Thread 对象
    Thread thread = new Thread(futureTask);
    // 启动线程
    thread.start();
    // 调用 FutureTask 对象的 get() 办法来取得线程执行完结后的返回值
    String s = futureTask.get();
    System.out.println(s);
}

}
代码块 123456789101112131415161718192021222324252627282930313233 运行后果:我是线程中返回的字符串
代码块 13. 线程休眠在后面介绍 Thread 类的罕用办法时,咱们介绍了 sleep() 静态方法,该办法能够使以后执行的线程睡眠(临时进行执行)指定的毫秒数。线程休眠的实例如下:/**

  • @author colorful@TaleLin
    */

public class SleepDemo implements Runnable {

@Override
public void run() {for (int i = 1; i <= 5; i ++) {
        // 打印语句
        System.out.println(Thread.currentThread().getName() + ": 执行第" + i + "次");
        try {
            // 使以后线程休眠
            Thread.sleep(1000);
        } catch (InterruptedException e) {e.printStackTrace();
        }
    }
}


public static void main(String[] args) {
    // 实例化 Runnable 的实现类
    SleepDemo sleepDemo = new SleepDemo();
    // 实例化线程对象
    Thread thread = new Thread(sleepDemo);
    // 启动线程
    thread.start();}

}
代码块 123456789101112131415161718192021222324252627282930 运行后果:Thread-0: 执行第 1 次
Thread-0: 执行第 2 次
Thread-0: 执行第 3 次
Thread-0: 执行第 4 次
Thread-0: 执行第 5 次
代码块 12345

  1. 线程的状态和生命周期 java.lang.Thread.Starte 枚举类中定义了 6 种不同的线程状态:NEW:新建状态,尚未启动的线程处于此状态;RUNNABLE:可运行状态,Java 虚拟机中执行的线程处于此状态;BLOCK:阻塞状态,期待监视器锁定而被阻塞的线程处于此状态;WAITING:期待状态,无限期期待另一线程执行特定操作的线程处于此状态;TIME_WAITING:定时期待状态,在指定等待时间内期待另一线程执行操作的线程处于此状态;TERMINATED:完结状态,已退出的线程处于此状态。值得注意的是,一个线程在给定的工夫点只能处于一种状态。这些状态是不反映任何操作系统线程状态的虚拟机状态。线程的生命周期,实际上就是上述 6 个线程状态的转换过程。下图展现了一个残缺的生命周期:
  2. 小结

通过本篇文章,咱们晓得了线程是操作系统可能进行运算调度的最小单位。线程也被称为轻量级过程。在 Java 中,能够以 3 种形式创立线程,别离是继承 Thread 类、实现 Runnable 接口以及实现 Callable 接口。能够应用静态方法 sleep() 让线程休眠。线程状态有 6 种,也有材料上说线程有 5 种,这部分内容咱们依照 Java 源码中的定义 6 种来记忆即可。

欢送关注「慕课网」,发现更多 IT 圈优质内容,分享干货常识,帮忙你成为更好的程序员!

正文完
 0