Java学习-----如何创建线程

在多线程编程中,创建线程是实现并发操作的基础。不同的编程语言和开发环境提供了多种创建线程的方式,每种方式都有其独特的原理、作用、优缺点。下面将详细介绍几种常见的创建线程的方式。​

(一)继承 Thread 类​

在 Java 中,Thread 类是线程操作的核心类,继承 Thread 类创建线程是一种基础方式。通过创建一个继承 Thread 类的子类,并重写其 run () 方法,然后实例化该子类对象,调用 start () 方法即可启动线程。​

Thread 类实现了 Runnable 接口,其中 run () 方法是线程的执行体。当调用 start () 方法时,Java 虚拟机会为该线程分配 CPU 资源等,使其进入就绪状态,当得到 CPU 时间片后,就会执行 run () 方法中的代码。​这种方式可以方便地创建线程,通过重写 run () 方法定义线程要执行的任务,实现线程的并发执行。​

Thread的优点主要有:​实现简单,直接继承 Thread 类,重写 run () 方法即可,无需额外实现接口。​

而缺点则主要有:Java 是单继承机制,一个类继承了 Thread 类后,就不能再继承其他类,灵活性较差。​

下面用一个案例简单的实现一下该继承方式:

java 复制代码
class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("线程" + Thread.currentThread().getName() + "执行:" + i);
        }
    }
}

public class ThreadTest {
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.setName("线程1");
        thread.start();
    }
}

在上述代码中,MyThread 类继承了 Thread 类,并重写了 run () 方法,在 run () 方法中定义了线程要执行的循环打印任务。在 main 方法中,创建了 MyThread 类的实例,设置线程名称后调用 start () 方法启动线程,线程启动后会执行 run () 方法中的内容。​

(二)实现 Runnable 接口​​

实现 Runnable 接口创建线程,是让一个类实现 Runnable 接口,并重写其 run () 方法,然后将该类的实例作为参数传递给 Thread 类的构造方法,最后调用 Thread 对象的 start () 方法启动线程。​

Runnable 接口中只包含一个 run () 方法,它定义了线程要执行的任务。当将实现了 Runnable 接口的对象传递给 Thread 类后,Thread 类的 run () 方法会调用该对象的 run () 方法,从而实现线程的执行。​这种方式可以让多个线程共享同一个实现了 Runnable 接口的对象,从而共享资源,提高资源的利用率。​

其优缺点主要为:​

优点:一个类可以同时实现多个接口,不影响其继承其他类,灵活性高;多个线程可以共享一个 Runnable 实例,适合多个线程处理同一份资源的场景。​

缺点:相对于继承 Thread 类,实现过程稍显复杂,需要多一步将 Runnable 实例传递给 Thread 类的操作。​

还是一样,用一个简单的案例来实现一下这个方法:

java 复制代码
class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("线程" + Thread.currentThread().getName() + "执行:" + i);
        }
    }
}

public class RunnableTest {
    public static void main(String[] args) {
        MyRunnable runnable = new MyRunnable();
        Thread thread1 = new Thread(runnable, "线程1");
        Thread thread2 = new Thread(runnable, "线程2");
        thread1.start();
        thread2.start();
    }
}

上述代码中,MyRunnable 类实现了 Runnable 接口,重写了 run () 方法。在 main 方法中,创建了 MyRunnable 实例,然后将其作为参数分别传递给两个 Thread 对象,设置不同的线程名称后启动线程。两个线程会共享 MyRunnable 实例,共同执行 run () 方法中的任务。​

(三)实现 Callable 接口​

Callable 接口是 Java 5 中引入的,与 Runnable 接口类似,但它可以返回线程执行的结果,并且可以抛出异常。实现 Callable 接口创建线程时,需要与 FutureTask 类配合使用。​

Callable 接口中的 call () 方法是线程的执行体,该方法有返回值且可以抛出异常。FutureTask 类实现了 Future 接口和 Runnable 接口,它可以接收 Callable 对象作为参数。当将 FutureTask 对象传递给 Thread 类并启动线程后,线程会执行 call () 方法,执行结果会被 FutureTask 保存,通过 FutureTask 的 get () 方法可以获取该结果。​​这种方式适合需要获取线程执行结果的场景,能够方便地处理线程执行过程中可能出现的异常。​

Callable优缺点​主要有

优点:可以获取线程执行结果,能抛出异常,功能更强大;同样遵循面向接口编程思想,不影响类的继承,灵活性高。​

缺点:实现过程相对复杂,需要结合 FutureTask 类使用,获取结果的 get () 方法可能会阻塞当前线程。​

下面是其的一个简单案例

java 复制代码
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 1; i <= 5; i++) {
            sum += i;
            System.out.println("线程" + Thread.currentThread().getName() + "执行:" + i);
        }
        return sum;
    }
}

public class CallableTest {
    public static void main(String[] args) {
        MyCallable callable = new MyCallable();
        FutureTask<Integer> futureTask = new FutureTask<>(callable);
        Thread thread = new Thread(futureTask, "线程1");
        thread.start();
        try {
            int result = futureTask.get();
            System.out.println("线程执行结果:" + result);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}

在该案例中,MyCallable 类实现了 Callable接口,重写了 call () 方法,该方法计算 1 到 5 的和并返回。创建 FutureTask 对象时将 MyCallable 实例传入,再将 FutureTask 对象作为参数创建 Thread 对象并启动线程。通过 futureTask.get () 方法可以获取 call () 方法的返回结果,该方法会阻塞当前线程直到获取到结果。​

(四)使用线程池​

线程池是一种线程管理机制,它预先创建一定数量的线程,当有任务需要执行时,从线程池中取出一个线程来执行任务,任务执行完成后,线程不会被销毁,而是返回到线程池中等待下一个任务。使用线程池创建线程,其实是从线程池中获取线程来执行任务。​

线程池内部维护了一个线程队列和任务队列。当提交任务时,如果线程池中有空闲线程,则立即执行任务;如果没有空闲线程,且当前线程数量未达到线程池的最大容量,则创建新线程执行任务;如果达到最大容量,则将任务放入任务队列等待,当有线程空闲时,再从任务队列中取出任务执行。​线程池可以减少线程的创建和销毁带来的开销,提高系统的性能和资源利用率,同时可以对线程进行统一管理和控制。​

线程池的优缺点主要有:​

优点:降低资源消耗,提高响应速度,便于线程管理(如控制最大并发数等)。​

缺点:线程池的参数设置需要根据实际情况进行调整,设置不当可能会影响性能;对于一些简单的任务,使用线程池可能会增加一定的复杂性。​

下面是一个简单的线程池实现:

java 复制代码
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

class MyTask implements Runnable {
    private int taskId;

    public MyTask(int taskId) {
        this.taskId = taskId;
    }

    @Override
    public void run() {
        System.out.println("任务" + taskId + "由线程" + Thread.currentThread().getName() + "执行");
    }
}

public class ThreadPoolTest {
    public static void main(String[] args) {
        // 创建一个固定大小为3的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 5; i++) {
            executorService.submit(new MyTask(i));
        }
        // 关闭线程池
        executorService.shutdown();
    }
}

在上面的代码中,我们通过 Executors.newFixedThreadPool (3) 创建了一个固定大小为 3 的线程池。然后循环提交 5 个 MyTask 任务到线程池,线程池会从内部的线程中分配线程来执行这些任务。任务执行完成后,线程会返回到线程池中。最后调用 shutdown () 方法关闭线程池。​

相关推荐
ATaylorSu6 分钟前
排序算法入门:直接插入排序详解
笔记·学习·算法·排序算法
野原鑫之祝7 分钟前
嵌入式开发学习———Linux环境下数据结构学习(五)
linux·c语言·数据结构·学习·vim·排序算法·嵌入式
Shun_Tianyou11 分钟前
Python Day19 时间模块 和 json模块 及例题分析
java·服务器·前端
颜挺锐19 分钟前
Java 课程,每天解读一个简单Java之水仙花数
java·开发语言
hello_ world.39 分钟前
RHCA学习概述
学习
2301_809561521 小时前
c++day5
java·c++·面试
晨非辰1 小时前
#C语言——学习攻略:深挖指针路线(四续)——函数指针数组--转移表
c语言·开发语言·经验分享·学习·visual studio
青云交1 小时前
「源力觉醒 创作者计划」_文心大模型 4.5 开源 28 天:从车间轴承到山村课堂的 AI 突围
java·模型微调·工业质检·文心 4.5·开源 ai·中小企业 ai·乡村教育
正在走向自律1 小时前
企业微信API接口发消息实战:从0到1的技术突破之旅
java·数据库·企业微信·accesstoken·agentid·corpid·企业微信发消息
Mr Aokey2 小时前
破茧 JDBC:MyBatis 在 Spring Boot 中的轻量实践录
java·spring boot·mybatis