【多线程】Java线程的三种创建方式比较

一、概述

在Java中,线程使用Thread关键字来表示,所有线程对象,都必须来自Thread类或者Thread类子类的实例。

1.Thread类的继承关系

在这里插入图片描述
从上图中可以看出Thread类实现了Runnable接口。

2.Thread类的构造方法总览

下面所有的构造方法均按其在源码中的顺序排列。

public Thread() {
        init(null, null, "Thread-" + nextThreadNum(), 0);
}
public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
}
Thread(Runnable target, AccessControlContext acc) {
        init(null, target, "Thread-" + nextThreadNum(), 0, acc, false);
}
public Thread(ThreadGroup group, Runnable target) {
        init(group, target, "Thread-" + nextThreadNum(), 0);
}
public Thread(String name) {
        init(null, null, name, 0);
}
public Thread(ThreadGroup group, String name) {
        init(group, null, name, 0);
}
public Thread(Runnable target, String name) {
        init(null, target, name, 0);
}
public Thread(ThreadGroup group, Runnable target, String name) {
        init(group, target, name, 0);
}
public Thread(ThreadGroup group, Runnable target, String name,
                  long stackSize) {
        init(group, target, name, stackSize);
}

3.Thread类常用的构造方法

//分配一个新的Thread对象。 此构造具有相同的效果Thread (null, null, gname) ,其中gname是新生成的名字。 自动生成的名称格式为"Thread-"+ n ,其中n为整数。 
public Thread() {
    init(null, null, "Thread-" + nextThreadNum(), 0);
}
//分配一个新的Thread对象。 此构造具有相同的效果Thread (null, null, name) 。 name为新线程的名称。
public Thread(String name) {
    init(null, null, name, 0);
}
//分配一个新的Thread对象。 该构造函数具有与Thread (null, target, gname)相同的效果,其中gname是新生成的名称。 自动生成的名称格式为"Thread-"+ n ,其中n为整数。target - 启动此线程时调用其run方法的对象。 如果null ,这个类run方法什么都不做。 
public Thread(Runnable target) {
    init(null, target, "Thread-" + nextThreadNum(), 0);
}
//分配一个新的Thread对象。 此构造具有相同的效果Thread (null, target, name)。target为启动此线程时调用其run方法的对象。 如果null ,则调用此线程的run方法。name为新线程的名称 
public Thread(Runnable target, String name) {
    init(null, target, name, 0);
}
//分配一个新的Thread对象。 此构造具有相同的效果Thread (group, null, name)。 
public Thread(ThreadGroup group, String name) {
    init(group, null, name, 0);
}
//分配一个新的Thread对象,使其具有target作为其运行对象,具有指定的name作为其名称,属于group引用的线程组。
public Thread(ThreadGroup group, Runnable target, String name) {
    init(group, target, name, 0);
}

二、线程的创建

1.继承Thread类,创建线程

通过继承Thread类来创建一个线程的一般步骤:

  • :定义一个类,继承Thread类;
  • :重写Thread类的run()方法,并将该线程要执行的操作声明在该方法中;
  • :创建该Thread子类对象的实例;
  • :调用start()方法,启动该线程。
//第一步:创建一个叫MyThread的类继承于Thread类
class MyThread extends Thread {
    //第二步:重写Thread类的run()方法
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());  //执行的操作就是打印该线程的名称
    }
}
//测试类
public class TestDemo {
    public static void main(String[] args) throws InterruptedException {
        //第三步:创建该Thread子类对象的实例
        MyThread myThread1 = new MyThread();
        //第四步:调用该线程的start()方法,启动该线程
        myThread1.start();

        MyThread myThread2 = new MyThread();
        myThread2.start();
    }
}
执行结果:
Thread-0
Thread-1

2.实现Runnable接口,创建线程

通过实现Runnable接口创建一个线程的步骤:

  • :定义一个类,实现Runnable接口;
  • :重写其run()方法,跟Thread类中的run()方法一样,将线程要执行的操作声明在该run()方法中;
  • :创建该Runnable实现类的实例;
  • :将该实例作为Thread的target来创建Thread对象,这个Thread对象即真正的线程对象;
  • :调用start()方法,启动该线程。
//第一步:创建Runnable接口的实现类MyThread
class MyThread implements Runnable {
    //第二步:重写run()方法
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());  //执行的操作就是打印该线程的名称
    }
}

public class TestDemo {
    public static void main(String[] args) throws InterruptedException {
        //第三步:创建Runnble()实现类的实例
        MyThread myThread1 = new MyThread();
        //第四步:将该实例传到Thread类的构造器中创建Thread对象
        Thread thread1 = new Thread(myThread1);
        //第五步:调用start()方法启动线程
        thread1.start();

        Thread thread2 = new Thread(myThread1);
        thread2.start();
    }
}
执行结果:
Thread-0
Thread-1

3.使用Callable和Future,创建线程

Callable的不同之处在于,与Runnable接口相比,Callable接口提供了一个call( )方法作为线程执行体,call()方法可以有返回值,可以声明抛出异常。
而Future接口则是来代表Callable接口里call()方法的返回值,并且Future接口还有一个实现类FutureTask,这个实现类既实现了Future接口,还实现了Runnable接口,因此可以作为Thread类的target。

使用Callable和Future,创建线程的步骤:

  • :定义一个类,实现Callable接口;
  • :重写其call()方法,跟上面重写run()方法一样,将线程要执行的操作声明在call()方法中;
  • :创建该Callable实现类的实例;
  • :将此对象传递到FuterTask构造器中,创建FuterTask 对象;
  • :将FutureTask对象作为Thread对象的target来创建线程(因为FutureTask实现了Runnable接口);
  • :调用start()方法,启动线程。
  • 注意:可以调用FutureTask对象的get()方法来获得线程执行结束后的返回值。
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
//第一步:创建Callable( )接口的实现类
class MyThread implements Callable {
    //第二步:重写其call()方法
    @Override
    public Object call() throws Exception {    //线程执行的操作就是求5的阶乘
        int sum = 1;
        for (int i = 5; i >= 1; i--) {
            sum *= i;
        }
        return sum;
    }
}

public class TestDemo {
    public static void main(String[] args) throws InterruptedException {
        //第三步:创建该Callable实现类的实例
        MyThread myThread = new MyThread();
        //第四步:将此对象传递到FuterTask构造器中,创建FuterTask 对象
        FutureTask futureTask = new FutureTask(myThread);
        //第五步:将FutureTask对象作为Thread对象的target来创建线程
        Thread thread1 = new Thread(futureTask);
        //第六步:调用start()方法,启动线程
        thread1.start();

        //获取线程的返回值
        try {
            System.out.println("5的阶乘结果为:" + futureTask.get());
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}
打印结果:
5的阶乘结果为:120

三、三种创建方式的比较

1.前两种方式比较

  • 相同之处:都需要重写run( )方法,并将线程执行的操作声明在run( )方法中;
  • 不同之处:第一种方式是继承Thread类,由于Java语言单继承的原因,如果它继承了Thread类,那么就没办法继承其它的类了。而第二种方式只是实现了Runnble( )接口,它还可以继续继承其他类。所以往往第二种在实际应用中更加广泛。

2.第三种方式与前两种方式的比较

第三种方式与前两种相比,它实现了Callable接口,并将线程的操作声明在其重写的call()方法中,而不是run()方法,并且在call()方法中可以有返回值,它还可以抛出异常。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值