进程:应用程序的执行实例,有独立的内存空间和系统资源
线程:CPU调度和分派的基本单位,进程中执行运算的最小单位,可完成一个独立的顺序控制流程
多线程:如果在一个进程中同时运行了多个线程,用来完成不同的工作,则称之为“多线程”。多个线程交替占用CPU资源,而非真正的并行执行
一个程序有一个进程。一个进程包含多个线程(必有一个主线程)
多线程好处:
-
充分利用CPU的资源
-
简化编程模型
-
带来良好的用户体验
主线程
Java
提供了 java.lang.Thread
类支持多线程编程
主线程
-
main()
方法即为主线程入口 -
产生其他子线程的线程
-
必须最后完成执行,因为它执行各种关闭动作
// 查看线程
public static void main(String args[]) {
Thread thread = Thread.currentThread();
System.out.println("当前线程是: " + thread.getName());
thread.setName("MyJavaThread");
System.out.println("当前线程名是: " + thread.getName());
}
当前线程是: main
当前线程名是: MyJavaThread
在 Java
中创建线程的 3 种方式
继承 java.lang.Thread
类
实现 java.lang.Runnable
接口
实现 java.util.concurrent.Callable
接口
使用线程的步骤
定义线程;创建线程对象;启动线程;终止线程
创建线程
方式1:Thread
1、继承 Thread
类创建线程
2、重写 run()
方法,编写线程执行体
3、创建线程对象,调用 start()
方法启动线程
public class MyThread extends Thread{
//重写run()方法
public void run(){
for(int i = 1; i <= 10; i++){
System.out.println(Thread.currentThread().getName()+" : "+i);
}
}
}
public static void main(String[] args) {
MyThread thread1 = new MyThread();
thread1.setName("线程1"); // 设置线程名
thread1.start(); // 启动线程
MyThread thread2 = new MyThread();
thread2.setName("线程2");
thread2.start();
}
多个线程交替执行,不是真正的“并行”,线程每次执行时长由分配的CPU时间片长度决定。
线程1 : 1
线程1 : 2
线程1 : 3
线程2 : 1
线程1 : 4
线程1 : 5
线程1 : 6
线程2 : 2
线程1 : 7
线程2 : 3
线程1 : 8
线程2 : 4
线程2 : 5
线程1 : 9
线程2 : 6
线程1 : 10
线程2 : 7
线程2 : 8
线程2 : 9
线程2 : 10
调用 run()
:只有主线程一条执行路径
调用 start()
:多条执行路径,主线程和子线程并行交替执行
方式2:Runnable
1、定义 MyRunnable
类实现 Runnable
接口
2、重写 run()
方法,编写线程执行体
3、创建 Thread
对象 new Thread(Runnable)
,调用 start()
方法启动线程
public class MyRunnable implements Runnable{
public void run(){
for(int i = 1; i <= 10; i++){
System.out.println(Thread.currentThread().getName()+" : "+i);
}
}
}
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread1 = new Thread(myRunnable);
thread1.start(); //启动线程
Thread thread2 = new Thread(myRunnable);
thread2.start();
}
方式3:Callable
1、定义 MyCallable
类实现 Callable<>
接口
2、重写 call()
方法,编写线程执行体(注意:有返回值)
3、使用 FutureTask
包装 Callable
对象,调用 start()
方法启动线程
4、使用 futureTask.get()
获取线程的返回值
import java.util.concurrent.Callable;
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
// 执行任务
for (int i = 1; i <= 10; i++) {
System.out.println(Thread.currentThread().getName() + " : " + i);
// 模拟一些工作
Thread.sleep(100); // 添加延迟以便观察线程执行
}
return "这是MyCallable";
}
}
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
// 测试
public class Test{
public static void main(String[] args) {
MyCallable myCallable = new MyCallable();
FutureTask<String> futureTask1 = new FutureTask<>(myCallable);
FutureTask<String> futureTask2 = new FutureTask<>(myCallable);
Thread thread1 = new Thread(futureTask1);
thread1.start();
Thread thread2 = new Thread(futureTask2);
thread2.start();
// 接收返回值
try {
System.out.println("futureTask1返回值: " + futureTask1.get());
System.out.println("futureTask2返回值: " + futureTask2.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
比较 Thread
Runnable
Callable
三种创建线程的方式
- 继承
Thread
类:适用于简单的线程任务,不需要重用线程逻辑。单继承 - 实现
Runnable
接口:适用于线程任务逻辑需要重用,且可以将任务与线程分离。避免单继承局限性 - 实现
Callable
接口:适用于需要线程计算结果,并能处理异常的场景。