零基础掌握Java多线程(一):进程与线程、线程的创建

 学习目的:使用Web开发,构建网站

一、进程与线程

为了解决多进程开销过大的问题,引入线程(Thread),轻量级进程。每一个进程都包含多个线程。可以同一时间执行多个任务。

1. 进程与线程关系

进程:操作系统资源分配的基本单位(如内存、文件),每个进程有独立内存空间。资源隔离性好,适合需高稳定性的任务(如独立服务)

线程:进程内的执行单元,多个线程共享进程资源(内存、文件等),是CPU调度的基本单位。资源共享效率高,适合高并发但需控制数量(如并行计算)。

2. 资源开销差异

进程开销大:创建/销毁需申请/释放资源(如内存),切换时需保存完整上下文(寄存器、内存管理等)。  

线程开销小:首个线程随进程创建时申请资源,后续线程直接共享资源;切换时仅需保存寄存器等少量数据,故称“轻量级进程”

3. 线程效率的局限性

资源限制:线程共享进程的有限资源(如内存)。提高线程数目,能提升效率,但无法线性增长,资源有限。

调度开销:线程数量过多时,操作系统调度线程的切换成本增加,反而降低性能。  线程数目太多,线程调度开销也会增大。

4. 稳定性影响  

进程独立:一个进程崩溃不影响其他进程

线程依赖:同一进程的线程共享资源,一个线程崩溃可能导致整个进程终止

二、线程的创建

1.基本创建软件包的流程(以IDEA为例)

新建软件包,并为软件包命名(以first命名为例)

在软件包中创建Java类

2.基本创建相关知识

多线程默认不按照顺序执行,具体顺序由操作系统调度策略综合决定。

创建启动线程的一般流程:

(1)​​定义线程任务​​:

通过重写run()方法实现线程的具体逻辑。run()方法是线程的执行体,是线程的核心。定义了线程启动后要执行的任务内容。可以通过继承Thread类并重写run()方法,或实现Runnable接口的run()方法来实现。Runnable接口是重写run(),Callable接口是重写call()。

(2)​​创建线程对象​​:

若继承Thread类,直接实例化该子类

若实现Runnable接口,需将Runnable实例作为参数传递给Thread的构造函数。

(3)​​启动线程​​:

调用线程对象的start()方法。该方法会触发JVM创建一个新线程,并自动调用run()方法执行任务。注意直接调用run()方法仅会在当前线程中同步执行,不会创建新线程。

start() vs run() 的区别

start():​​创建​​新线程,并​​安排​​执行run()方法

run():只是​​普通方法​​,直接调用不会创建线程

三、线程创建的方法

1.继承Thread类--单继承

注:Java只支持单继承,不支持多继承。所以该方法有单继承限制。

(1)基本写法

class MyThread extends Thread {
    public void run() {
        // 任务逻辑(无返回值)
    }
}
new MyThread().start();

代码示例:

package thread;

/**
 * 自定义线程类,继承Thread类并重写run方法定义线程任务
 * 用于演示继承Thread类创建线程的基本方式
 */
class MyThread extends Thread{
    @Override
    public void run(){
        //线程执行体:无限循环打印当前的线程信息
        while (true){
            //打印当前线程对象(包含线程名称、优先级等信息)
            System.out.println("Using Thread:"+Thread.currentThread());
            try{
                //休眠一秒(1000毫秒),sleep()会抛出InterruptedException,需要捕获处理
                Thread.sleep(1000);
            }catch(InterruptedException e){
                //当线程被中断时,抛出运行时异常终止循环
                throw new RuntimeException(e);
            }
        }
    }
}

/**
 * 主类演示通过继承Thread类创建并启动线程
 */
public class ThreadDemo {
    public static void main(String[] args) {
        Thread t = new MyThread(); //创建自定义线程实例

        //启动线程,调用start(),而不是run()
        //start()会创建新线程并自动调用run()方法,实现并发执行
        //直接调用run()只会作为普通方法在当前线程同步执行
        t.start();
    }
}

(2)基于继承Thread,使用内部匿名类

new Thread() {
    @Override
    public void run() {
        // 线程任务
    }
}.start();

代码示例:

package thread;

public class AnonymousThreadDemo {
    public static void main(String[] args) {
        //通过匿名内部类创建并启动线程
        new Thread(){ //创建Thread类的匿名子类
            @Override
            public void run(){
                System.out.println("Anonymous Thread running");
                try {
                    Thread.sleep(1000); //休眠一秒
                } catch (InterruptedException e) {
                    //处理中断异常
                    e.printStackTrace(); //打印异常堆栈
                }
            }
        }.start();  //立即启动线程
    }
}

2.实现Runnable接口--无返回值任务

(1)基本写法


class MyTask implements Runnable {
    public void run() {
        // 任务逻辑
    }
}
new Thread(new MyTask()).start();

示例代码:

package thread;

/**
 * 自定义Runnable实现类,用于定义线程任务逻辑
 */
class MyRunnable implements Runnable{
    @Override
    public void run(){
        //使用无限循坏持续运行
        while (true){
            //打印当前线程信息(包含线程名称、优先级等)
            System.out.println("Runnable Task:"+Thread.currentThread());
            try {
                Thread.sleep(1000); //休眠一秒
            } catch (InterruptedException e) {
                //捕获并打印中断异常(当线程中断时触发)
                e.printStackTrace();
            }
        }
    }
}

/**
 * 演示Runnable接口使用的示例类
 * 展示如何通过Runnable实现多线程编程
 */
public class RunnableDemo {
    public static void main(String[] args) {
        Runnable task = new MyRunnable(); //创建Runnable实例

        //创建Thread对象,讲Runnable任务与线程绑定
        //第二个参数(可不加),指定线程名称是"Runnable-Thread"
        Thread thread = new Thread(task,"Runnable-Thread");
        thread.start();//启动线程,JVM会调用run()方法
    }
}

(2)基于实现Runnable,实现匿名内部类


new Thread(new Runnable() {
    @Override
    public void run() {
        // 线程任务
    }
}).start();

代码示例:

package thread;

public class AnonymousRunnableDemo {
    public static void main(String[] args) {
        //使用匿名内部类实现Runnable接口,并直接传递给Thread构造函数
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    System.out.println("Anonymous Runnable running");
                    try {
                        Thread.sleep(1000);
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }
}

3.实现Callable接口--有返回值任务

可返回结果 + 抛出异常,需配合 FutureTask

(1)基本方法


// 1. 定义一个实现Callable接口的类,泛型指定返回值类型

class 任务名 implements Callable<返回类型> { 

    public 任务名(参数) {

        // 初始化字段

    }

// 2. 重写call方法,编写任务逻辑

    @Override

    public 返回值类型 call() throws Exception {

        // 代码逻辑

        // 可以返回结果,也可以抛出异常

        return 返回类型对应的对象;

    }

}

// 3.将Callable任务包装进FutureTask

FutureTask<返回值类型> futureTask = new FutureTask<>(callableTask);

// 4.创建线程并启动

Thread thread = new Thread(futureTask);

thread.start();

代码示例:

package thread;

// 导入必要的并发编程类
import java.util.concurrent.Callable;       // 定义可返回结果和抛出异常的任务的接口
import java.util.concurrent.ExecutionException; // 执行计算时可能抛出的异常
import java.util.concurrent.FutureTask;    // 可取消的异步计算,提供了Future的基本实现

/**
 * MyTask 类实现了 Callable<Integer> 接口
 * 这意味着它可以作为一个能够返回 Integer 类型结果的任务
 * 与 Runnable 相比,Callable 的优势在于可以返回值和抛出受检异常
 */
class MyTask implements Callable<Integer> {
    private int n; // 用于存储计算的上限值

    /**
     * 构造函数,初始化任务的计算上限值
     * @param n 计算的上限值(计算从0到n-1的和)
     */
    public MyTask(int n) {
        this.n = n; // 将参数n赋值给成员变量n
    }

    /**
     * 重写 Callable 接口的 call 方法
     * 这是任务执行的核心逻辑,计算从0到n-1的累加和
     * @return 计算得到的累加和 (Integer类型)
     * @throws Exception 可以抛出受检异常,这是Callable与Runnable的一个区别
     */
    @Override
    public Integer call() throws Exception {
        int sum = 0; // 初始化累加和为0
        // 循环从0到n-1,进行累加计算
        for (int i = 0; i < n; i++) {
            sum += i; // 将当前索引i的值加到sum上
        }
        return sum; // 返回最终计算得到的累加和
    }
}

/**
 * CallableDemo 类演示了如何使用 Callable 和 FutureTask
 * 实现带有返回值的多线程任务
 */
public class CallableDemo {
    public static void main(String[] args) {
        // 创建一个MyTask实例,计算0到79的和(因为参数是80,循环条件是i<80)
        MyTask task = new MyTask(80);

        /**
         * 创建一个FutureTask对象,封装MyTask任务
         * FutureTask实现了RunnableFuture接口,而RunnableFuture继承了Runnable和Future接口
         * 因此FutureTask既可以作为Runnable被Thread执行,又可以作为Future来获取计算结果
         */
        FutureTask<Integer> future = new FutureTask<>(task);

        /**
         * 创建并启动一个线程来执行FutureTask任务
         * FutureTask本身实现了Runnable接口,所以可以传递给Thread的构造函数[9](@ref)
         * 也可以直接写成 new Thread(future).start();
         */
        Thread thread = new Thread(future); // 创建线程,将future作为其运行目标
        thread.start(); // 启动线程,线程开始执行MyTask中的call方法

        // 尝试获取并处理计算结果
        try {
            Integer result = future.get();
            // 打印计算结果:0+1+2+...+79 = 3160
            System.out.println(result);
        } catch (InterruptedException e) {
            // 处理线程在等待过程中被中断的异常
            e.printStackTrace();
        } catch (ExecutionException e) {
            /**
             * 处理执行计算过程中发生的异常
             * 如果在MyTask的call方法执行过程中抛出了任何异常,
             * 都会被包装在ExecutionException中
             */
            e.printStackTrace();
        }
    }
}

(2)匿名内部类实现Callable接口


这种方式适用于一次性任务,无需单独创建类文件。直接在需要的地方(例如 main方法内)定义并实例化 Callable。

package thread;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class AnonymousCallableDemo {
    public static void main(String[] args) {
        Callable<Integer> callable = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                int sum = 0;
                for (int i = 0; i < 80; i++) {
                    sum+=i;
                }
                return sum;
            }
        };
        FutureTask<Integer> futureTask = new FutureTask<>(callable);
        Thread thread = new Thread(futureTask);
        thread.start();
        try {
            Integer result = futureTask.get();
            System.out.println(result);  //3160
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

【推荐】基于 Lambda 表达式(JDK 8+)

(1)Lambda表达式基础

Lambda 表达式是 Java 8 引入的核心特性,用于简化函数式接口(仅含一个抽象方法的接口)的实现。其简化规则涵盖语法、参数、方法体等多个维度

基本语法


(parameters) -> expression

(parameters) -> { statements; }

(2)使用lambda表达式创建线程

继承Thread类的方式无法使用Lambda表达式简化​​, Callable和Runnable都是函数式接口(只有一个 call()方法),可以用 Lambda 表达式进一步简化

Lambda 中引用的外部变量必须是 final 或 effectively final。

1)使用Lambda表达式实现Runnable接口

使用Lambda表达式:


Runnable lambdaRunnable = () -> {

        //线程执行代码逻辑

};

new Thread(lambdaRunnable).start();

更简洁的直接写法:

// 创建并启动线程
new Thread(() -> {
    // 线程执行的代码逻辑
}).start();

代码示例:

package thread;

public class LambdaRunnableDemo1 {
    public static void main(String[] args) {
        //使用Lambda表达式实现Runnable接口
        Runnable lambdaRunnable = ()->{
            //无限打印线程名
            while (true){
                System.out.println(Thread.currentThread().getName());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

        //创建并启动线程,第二个参数(对线程的命名可省略)
        new Thread(lambdaRunnable,"Lambda_Thread").start();
    }
}
package thread;

public class LambdaRunnableDemo2 {
    public static void main(String[] args) {
        final String Hi = "Hello Lambda Runnable";

        new Thread(()->{
            System.out.println(Hi + Thread.currentThread().getName());
        },"Lambda-Thread").start();
    }
}

单行表达式简化:若执行语句仅一行,可省略大括号,进一步简化为:() -> 执行语句

Runnable runnable = () -> System.out.println("Hello from Lambda");
new Thread(runnable).start();

或直接内联

new Thread(() -> System.out.println("Hello from Lambda")).start();
2)使用Lambda实现Callable接口

Callable<返回类型> callable = () -> {
    // 任务逻辑代码
    return 结果; // 返回指定类型的结果
};

package thread;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class LambdaCallableDemo {
    public static void main(String[] args) {

        //1. 使用Lambda 表达式实现Callable接口
        Callable<Integer> MyTask = ()->{
            int sum = 0;
            for (int i = 0; i < 80; i++) {
                sum+=i;
            }
            return sum;
        };

        //2.将Callable任务包装在FutureTask中
        FutureTask<Integer> futureTask = new FutureTask<>(MyTask);

        //3.创建线程来执行任务
        Thread thread = new Thread(futureTask);
        thread.start();

        //4.通过FutureTask获取结果
        try{
            Integer result = futureTask.get();
            System.out.println(result); //结果为3160
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

    }
}

单行表达式简化​:Lambda表达式主体为单行时,可自动返回该表达式的值,无需显式写return

Callable<Integer> callable = () -> 42; // 无参数且返回简单值,直接返回常量
Callable<Double> randomTask = () -> Math.random(); // 返回随机数

注意事项:

1.​​Lambda表达式限制​​:Lambda中引用的外部变量必须是final或effectively final(即实际不可变)。

2.线程启动​​:必须调用start()而非直接调用run(),后者会在当前线程同步执行。

3.​​资源管理​​:Callable需配合FutureTask或线程池使用,避免手动创建线程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值