Java 基础之线程以及三种创建线程的方式


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

简介

线程是一种重要的概念,用于实现并发执行的多任务。

  • 线程是程序执行的最小单元,它可以独立执行代码片段。

  • 多线程允许在同一程序中同时执行多个任务,提高程序的并发性和响应能力。

  • 线程可以共享内存空间,方便数据交换和通信。

三种方式创建线程

1. 继承Thread类

可以通过继承 Thread 类,并重写 run() 方法,在需要的地方调用 start 方法进行执行线程。

注意:需要调用 start 方法才能看见线程效果,调用 run 方法只是在当前线程同步执行 run 方法。

如果需要执行的逻辑代码只在一个地方出现,那么可以使用 Lambda 表达式的方式给

public class ThreadTest {

    /**
     * 继承自 Thread 的类
     * 1. 需要调用 start 方法才能看见线程效果,调用 run 方法只是在当前线程同步执行 run 方法
     */
    public static void main(String[] args) {
        new MyThread("线程1", 5).start();

        new MyThread("线程2", 2).start();
    }
}

class MyThread extends Thread {
    int count;

    MyThread(String name, int count){
        super(name);
        this.count = count;
    }

    @Override
    public void run() {
        System.out.println(getName() + "开始执行,时间:" + new Date());
        try {
            for (int i=0; i < count; i++){
                System.out.println(getName() + "第" + (i+1) + "次执行....");
                Thread.sleep(1000);
            }

        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(getName() + "执行完毕,时间:" + new Date());
    }
}

2. 实现Runnable接口

首先需要注意的是,实现 Runnable 接口的方式,其实还是要将实现类作为构造参数传递给 Thread 类,通过 Thread 的 start 方法开辟一个线程。

另外,Runnable 接口是一个函数式接口,可以通过 Lambda 表达式创建实现类。

public class RunnableTest {

    /**
     * 为什么需要通过Thread执行Runnable对象
     * 1. 线程管理:Thread类提供了对线程的管理和控制。它包含了一些方法,例如start()来启动线程,join()来等待线程完成,
     *      interrupt()来中断线程等。通过Thread类,可以更方便地控制线程的生命周期和执行。
     * 2. 线程调度:Thread类提供了线程调度的功能。线程调度是指决定线程执行顺序和时间片分配的过程。
     *      通过Thread类,可以设置线程的优先级、睡眠时间和调度策略等。
     * 3. 线程上下文:Thread类维护了线程的上下文信息,例如线程名称、线程状态、线程组等。
     *      通过Thread类,可以方便地获取和设置这些信息。
     */
    public static void main(String[] args) {
        MyRunnable runnable1 = new MyRunnable("线程1", 5);
        Thread thread1 = new Thread(runnable1);
        thread1.start();

        // Lambda 表达式创建线程
        new Thread(() -> {
            System.out.println( "线程2开始执行,时间:" + new Date());
            try {
                for (int i=0; i < 2; i++){
                    System.out.println("线程2第" + (i+1) + "次执行....");
                    Thread.sleep(1000);
                }

            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("线程2执行完毕,时间:" + new Date());
        }).start();
    }
}

class MyRunnable implements Runnable {
    private String name;
    private int count;

    MyRunnable(String name, int count) {
        this.name = name;
        this.count = count;
    }

    @Override
    public void run() {
        System.out.println(name + "开始执行,时间:" + new Date());
        try {
            for (int i=0; i < count; i++){
                System.out.println(name + "第" + (i+1) + "次执行....");
                Thread.sleep(1000);
            }

        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(name + "执行完毕,时间:" + new Date());
    }
}

3. 实现Callable接口

前面两种方式都是通过重写 run 方法实现多线程,需要注意的是 run 方法返回值是 void,也就是说无法获取线程的执行结果,于是 Callable 出现了。

这种方式需要通过线程池的方式进行调用,首先需要创建一个线程池,然后创建一个 Callable 接口的实现类,使用线程池的 submitexecute 执行线程中的代码。

而这两个方法的区别是, execute 方法是无返回值的,效果和上面的方式一样。而 submit 是有返回值的,返回的是一个 Future<T> 接口类型的对象,通过该接口的 get 方法可以获取到返回值。

public class CallableTest {

    /**
     *
     * @param args
     */
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(2);
        System.out.println("实际创建的线程池对象:" + executor);

        Callable<String> task1 = new MyCallable("线程1", 5);
        Future<String> future1 = executor.submit(task1);
        System.out.println("实际创建的Future对象:" + future1);

        Callable<String> task2 = new MyCallable("线程2", 2);
        Future<String> future2 = executor.submit(task2);

        executor.shutdown();

        try {
            // 这里是阻塞的,需要等待获取结果
            String result1 = future1.get();
            System.out.println(result1);

            // 虽然线程2比线程1早执行完,但是需要等待线程1执行完毕。如果放在线程1之前获取返回值,则先打印线程2执行完毕
            String result2 = future2.get();
            System.out.println(result2);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class MyCallable implements Callable<String> {
    private String name;
    private int count;

    public MyCallable(String name, int count) {
        this.name = name;
        this.count = count;
    }

    @Override
    public String call() throws Exception {
        //System.out.println(name + "开始执行,时间:" + new Date());
        for (int i=0; i < count; i++){
            System.out.println(name + "第" + (i+1) + "次执行....");
            Thread.sleep(1000);
        }
        //System.out.println(name + "执行完毕,时间:" + new Date());
        return name + "执行完毕!!!!!!!";
    }
}

提醒:本文发布于402天前,文中所关联的信息可能已发生改变,请知悉!

Tips:清朝云网络工作室

阅读剩余
THE END