原创

Java创建线程的三种方式详解

前言

JAVA创建线程一共有三种方式,Thread类,Runable接口,Callable接口。

继承Thread类

创建并启动多线程步骤

  1. 继承Thread类,并重写run()方法,该run()方法的方法体就代表了线程需要完成的任务。可以把run()方法称为线程执行体。
  2. 创建Thread子类实例。
  3. 调用线程对象的start()方法来启动该线程。

代码示例

/**
 * @ClassName: FirstThread
 * @Description: 集成Thread方式实现多线程
 * @author: 陈琼
 * @connect:907172474@qq.com
 * @date: 2021年2月11日 下午2:20:17
 */
public class FirstThread extends Thread {
    private int i;

    public void run() {
        for (; i < 20; i++) {
            System.out.println(getName() + " " + i);
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 20; i++) {
            // System.out.println(Thread.currentThread().getName() + " " + i);
            if (i == 5) {
                new FirstThread().start();
                new FirstThread().start();
            }
        }
    }
}

输出结果:

Thread-0 0
Thread-1 0
Thread-0 1
Thread-1 1
Thread-0 2
Thread-0 3
Thread-0 4
Thread-0 5
Thread-0 6
Thread-0 7
Thread-0 8
Thread-0 9
Thread-0 10
Thread-0 11
Thread-0 12
Thread-0 13
Thread-0 14
Thread-0 15
Thread-0 16
Thread-0 17
Thread-0 18
Thread-0 19
Thread-1 2
Thread-1 3
Thread-1 4
Thread-1 5
Thread-1 6
Thread-1 7
Thread-1 8
Thread-1 9
Thread-1 10
Thread-1 11
Thread-1 12
Thread-1 13
Thread-1 14
Thread-1 15
Thread-1 16
Thread-1 17
Thread-1 18
Thread-1 19

实现Runnable接口

  1. 实现Runable接口,并实现run方法;
  2. 创建Runable实例,并将此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
  3. 通过Thread对象创建线程后,调用start()方法启动线程。
/**
 * @ClassName: SecondThread
 * @Description: 实现Runable接口方式实现多线程
 * @author: 陈琼
 * @connect:907172474@qq.com
 * @date: 2021年2月11日 下午2:31:19
 */
public class SecondThread implements Runnable {
    private int i;

    @Override
    public void run() {
        for (; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + " " + i);
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            // System.out.println(Thread.currentThread().getName() + " " + i);
            if (i == 20) {
                SecondThread st = new SecondThread();
                new Thread(st, "新线程1").start();
                new Thread(st, "新线程2").start();
            }
        }
    }
}

输出结果:

新线程1 0
新线程2 0
新线程1 1
新线程2 2
新线程2 4
新线程2 5
新线程1 3
新线程2 6
新线程1 7
新线程2 8
新线程2 10
新线程2 11
新线程2 12
新线程2 13
新线程1 9
新线程2 14
新线程1 15
新线程2 16
新线程2 18
新线程1 17
新线程2 19

通过Callable和Future创建线程

  1. 创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。
  2. 创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。
  3. 使用FutureTask对象作为Thread对象的target创建并启动新线程。
  4. 调用FutureTask对象的get()方法来获得子线程执行结束后的返回值。

Lambda写法

/**
 * @ClassName: ThirdThread
 * @Description: Callable和FeatureTask实现线程Lambda写法
 * @author: 陈琼
 * @connect:907172474@qq.com
 * @date: 2021年2月11日 下午3:28:13
 */
public class ThirdThread {
    public static void main(String[] args) {

        FutureTask<Integer> task = new FutureTask<Integer>((Callable<Integer>) () -> {
            int i = 0;
            for (; i < 20; i++) {
                System.out.println(Thread.currentThread().getName() + " 循环变量i的值:" + i);
            }
            return i;
        });

        for (int i = 0; i < 20; i++) {
            System.out.println(Thread.currentThread().getName() + " 的循环变量i的值:" + i);
            if (i == 5) {
                new Thread(task, "线程1").start();
            }
        }

        try {
            System.out.println("线程1:" + task.get());
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

输出结果

main 的循环变量i的值:0
main 的循环变量i的值:1
main 的循环变量i的值:2
main 的循环变量i的值:3
main 的循环变量i的值:4
main 的循环变量i的值:5
main 的循环变量i的值:6
main 的循环变量i的值:7
main 的循环变量i的值:8
main 的循环变量i的值:9
main 的循环变量i的值:10
main 的循环变量i的值:11
main 的循环变量i的值:12
main 的循环变量i的值:13
main 的循环变量i的值:14
main 的循环变量i的值:15
main 的循环变量i的值:16
main 的循环变量i的值:17
main 的循环变量i的值:18
main 的循环变量i的值:19
线程1 循环变量i的值:0
线程1 循环变量i的值:1
线程1 循环变量i的值:2
线程1 循环变量i的值:3
线程1 循环变量i的值:4
线程1 循环变量i的值:5
线程1 循环变量i的值:6
线程1 循环变量i的值:7
线程1 循环变量i的值:8
线程1 循环变量i的值:9
线程1 循环变量i的值:10
线程1 循环变量i的值:11
线程1 循环变量i的值:12
线程1 循环变量i的值:13
线程1 循环变量i的值:14
线程1 循环变量i的值:15
线程1 循环变量i的值:16
线程1 循环变量i的值:17
线程1 循环变量i的值:18
线程1 循环变量i的值:19
线程1:20

实现接口写法

/**
 * @ClassName: ThirdThread2
 * @Description: Callable和FeatureTask实现线程实现接口写法写法
 * @author: 陈琼
 * @connect:907172474@qq.com
 * @date: 2021年2月11日 下午3:50:52
 */
public class ThirdThreadIntf implements Callable<Integer> {
    int i = 0;

    @Override
    public Integer call() throws Exception {
        for (; i < 20; i++) {
            try {
                Thread.sleep(new Random().nextInt(100));
                System.out.println(Thread.currentThread().getName() + " 循环变量i的值:" + i);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(Thread.currentThread().getName() + "执行完成");
        return i;
    }

    public static void main(String[] args) {
        Callable a = new ThirdThreadIntf();
        Callable b = new ThirdThreadIntf();
        FutureTask<Integer> task1 = new FutureTask<>(a);
        FutureTask<Integer> task2 = new FutureTask<>(a);
        FutureTask<Integer> task3 = new FutureTask<>(b);
        new Thread(task1, "线程一").start();
        new Thread(task2, "线程二").start();
        new Thread(task3, "线程三").start();
    }
}

执行结果:

线程一 循环变量i的值:0
线程三 循环变量i的值:0
线程二 循环变量i的值:1
线程三 循环变量i的值:1
线程三 循环变量i的值:2
线程一 循环变量i的值:2
线程一 循环变量i的值:3
线程二 循环变量i的值:4
线程三 循环变量i的值:3
线程三 循环变量i的值:4
线程一 循环变量i的值:5
线程二 循环变量i的值:6
线程三 循环变量i的值:5
线程二 循环变量i的值:7
线程二 循环变量i的值:8
线程三 循环变量i的值:6
线程一 循环变量i的值:9
线程二 循环变量i的值:10
线程一 循环变量i的值:11
线程三 循环变量i的值:7
线程三 循环变量i的值:8
线程二 循环变量i的值:12
线程一 循环变量i的值:13
线程二 循环变量i的值:14
线程三 循环变量i的值:9
线程二 循环变量i的值:15
线程一 循环变量i的值:16
线程三 循环变量i的值:10
线程二 循环变量i的值:17
线程一 循环变量i的值:18
线程三 循环变量i的值:11
线程一 循环变量i的值:19
线程一执行完成
线程二 循环变量i的值:20
线程二执行完成
线程三 循环变量i的值:12
线程三 循环变量i的值:13
线程三 循环变量i的值:14
线程三 循环变量i的值:15
线程三 循环变量i的值:16
线程三 循环变量i的值:17
线程三 循环变量i的值:18
线程三 循环变量i的值:19
线程三执行完成

说明:

  1. 以上线程一与线程三共享i变量,两个线程连续基数;
  2. 这种使用FeatureTack和Callable方式实现线程,多线程之间共享数据时Feature需要使用同一个Callable对象,如上task1和task2;如果使用尝试Thread使用同一个Feature实现多线程数据共享会发现只执行一个,这是由于FutureTask内Callable对象的状态已经不是NEW(请自行源码)。使用Feature和Callable实现多线程时,线程Thread的target是Callable对象而不是Feature,只有使用相同的target才可实现线程间数据共享

错误代码如下:

/**
 * @ClassName: ThirdThread2
 * @Description: Callable和FeatureTask实现线程实现接口写法写法
 * @author: 陈琼
 * @connect:907172474@qq.com
 * @date: 2021年2月11日 下午3:50:52
 */
public class ThirdThreadIntf implements Callable<Integer> {
    int i = 0;

    @Override
    public Integer call() throws Exception {
        for (; i < 20; i++) {
            try {
                Thread.sleep(new Random().nextInt(100));
                System.out.println(Thread.currentThread().getName() + " 循环变量i的值:" + i);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(Thread.currentThread().getName() + "执行完成");
        return i;
    }

    public static void main(String[] args) {
        Callable a = new ThirdThreadIntf();
        //Callable b = new ThirdThreadIntf();
        FutureTask<Integer> task1 = new FutureTask<>(a);
        //FutureTask<Integer> task2 = new FutureTask<>(a);
        //FutureTask<Integer> task3 = new FutureTask<>(b);
        new Thread(task1, "线程一").start();
        new Thread(task1, "线程二").start();
        new Thread(task1, "线程三").start();
    }
}

执行结果:

线程一 循环变量i的值:0
线程一 循环变量i的值:1
线程一 循环变量i的值:2
线程一 循环变量i的值:3
线程一 循环变量i的值:4
线程一 循环变量i的值:5
线程一 循环变量i的值:6
线程一 循环变量i的值:7
线程一 循环变量i的值:8
线程一 循环变量i的值:9
线程一 循环变量i的值:10
线程一 循环变量i的值:11
线程一 循环变量i的值:12
线程一 循环变量i的值:13
线程一 循环变量i的值:14
线程一 循环变量i的值:15
线程一 循环变量i的值:16
线程一 循环变量i的值:17
线程一 循环变量i的值:18
线程一 循环变量i的值:19
线程一执行完成

三种实现方式对比

使用Runnable、Callable接口的方式

  • 优势:线程类只是实现了Runnable接口或Callable接口,还可以集成其他类。
  • 优势:多个线程可以共享一个target对象,非常适合多线程来处理同一份资源的情况,实现线程间数据共享。
  • 劣势:编程稍微复杂,如果要获取当前线程,则必须使用Thread.currentThread()。

使用集成Thread类方式

  • 劣势:因为线程已经集成了Thread类,所以不能再集成其他类了。
  • 优势:编写简单,如果要获取当前线程,无需使用Thread.currentThread(),直接使用this即可获得当前线程。
正文到此结束
本文目录