学习目的:使用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或线程池使用,避免手动创建线程。
2503

被折叠的 条评论
为什么被折叠?



