一、什么是线程?线程和进程的区别?
线程:是进程的一个实体,是 cpu 调度和分派的基本单位,是比进程更小的可以独立运行的基本单位。
进程:具有一定独立功能的程序关于某个数据集合上的一次运行活动,是操作系统进行资源分配和调度的一个独立单位。
特点:线程的划分尺度小于进程,这使多线程程序拥有高并发性,进程在运行时各自内存单元相互独立,线程之间 内存共享,这使多线程编程可以拥有更好的性能和用户体验
注意:多线程编程对于其它程序是不友好的,占据大量 cpu 资源。
二、创建线程的4种方式
- 继承 Thread 类。
- 实现 Runnable 接口。
- 实现 Callable 接口。
- 从线程池中获取。
三、示例代码
继承 Thread 类
实现步骤:
多线程的实现步骤:
1. 定义类,然后继承Thread类。
2. 重写Thread类的run方法,要在run方法中定义线程要执行的任务。
3. 创建Thread的子类对象
4. 通过Thread子类对象调用start方法,启动线程。
Thread中的start方法:
void start():线程启动,线程会执行自己的run方法。
示例代码:
package com.xuyaxu.创建多线程.继承Thread类方式;
/**
* 测试类
*
* @author xyx
* @date 2021/4/14 10:34
*/
public class Demo {
public static void main(String[] args) {
// 创建自定义线程对象
MyThread myThread = new MyThread("新的线程!");
// 开启新线程
myThread.start();
MyThread myThread2 = new MyThread();
myThread2.start();
// 在主方法中执行for循环
for (int i = 0; i < 200; i++) {
System.out.println("main线程!" + i);
}
}
}
子类:
package com.xuyaxu.创建多线程.继承Thread类方式;
/**
* 自定义线程类
*
* @author xyx
* @date 2021/4/14 10:35
*/
public class MyThread extends Thread {
/**
* 定义指定线程名称的构造方法
*
* @param name 线程的名称
*/
public MyThread(String name) {
// 调用父类的String参数的构造方法,指定线程的名称
super(name);
}
/**
* 不指定线程的名字,线程有默认的名字Thread-0
*/
public MyThread() {
}
/**
* 重写run方法,完成该线程执行的逻辑
*/
@Override
public void run() {
for (int i = 0; i < 200; i++) {
System.out.println(getName() + ":正在执行!" + i);
}
}
}
实现 Runnable 接口
步骤:
多线程的第二种实现方式:
1. 定义类,实现Runnable接口。
2. 重写Runnable接口中的run方法,在run方法中定义线程要执行的任务。
3. 创建Runnable接口的实现类对象。
4. 创建Thread线程对象,在构造方法中将Runnable接口的实现对象作为参数传递。
5. 通过Thread线程对象调用start方法启动线程,线程会执行自己的run方法。
示例代码:
package com.xuyaxu.创建多线程.实现Runnable接口方式;
/**
* 测试类
*
* @author xyx
* @date 2021/4/14 11:07
*/
public class Demo {
public static void main(String[] args) {
// 创建自定义类对象,线程任务对象
MyRunnable myRunnable = new MyRunnable();
// 创建线程对象(真正的线程对象)
Thread thread = new Thread(myRunnable, "小强");
thread.start(); // 线程对象调用start方法启动线程
// 在主方法中执行for循环
for (int i = 0; i < 20; i++) {
System.out.println("旺财 " + i);
}
}
}
实现类:
package com.xuyaxu.创建多线程.实现Runnable接口方式;
/**
* 类的描述
*
* @author xyx
* @date 2021/4/14 11:08
*/
public class MyRunnable implements Runnable {
/**
* 重写runnable接口中的方法
*/
@Override
public void run() {
// 定义线程要执行的任务
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}
还可以通过匿名内部类方式实现Runnable接口:
package com.xuyaxu.创建多线程.实现Runnable接口方式;
/**
* 使用匿名内部类方式实现Runnable接口,建立多线程
*
* @author xyx
* @date 2021/4/14 11:58
*/
public class NoNameInnerClassThread {
public static void main(String[] args) {
// 这个整体 相当于new MyRunnable()
/*new Runnable() {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("匿名内部类:" + i);
}
}
};*/
Runnable runnable = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("张宇:" + i);
}
}
};
new Thread(runnable).start();
for (int i = 0; i < 20; i++) {
System.out.println("费玉清:" + i);
}
}
}
实现 Callable 接口
实现步骤:
多线程的第三种实现方式:
1. 定义类,实现Callable接口。
2. 重写Callable接口中的call方法,在call方法中定义线程要执行的任务(业务代码)。
3. 创建Callable接口的实现类对象。
4. 使用FutureTask类包装Callable接口的实现类对象
5. 以FutureTask的对象作为Thread的参数来创建线程,并启动。
6. 调用FutureTask对象的get()方法获得返回值,需要处理抛出的异常。
示例代码:
package 多线程.多线程的4种创建方式.实现Callable接口方式;
import java.util.concurrent.Callable;
/**
* 实现Callable接口
*
* @author xuyaxu
* @date 2023/3/5 18:11
*/
public class MyCallable implements Callable<String> {
/**
* call方法中放业务代码
*
* @return
* @throws Exception
*/
@Override
public String call() throws Exception {
return "重写的call方法执行了";
}
}
package 多线程.多线程的4种创建方式.实现Callable接口方式;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* 测试Callable接口的测试类
*
* @author xuyaxu
* @date 2023/3/5 18:13
*/
public class CallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 创建实现了Callable接口的类的实例对象
MyCallable myCallable = new MyCallable();
// 使用FutureTask类包装Callable接口的实现类对象myCallable
FutureTask<String> futureTask = new FutureTask<>(myCallable);
// 以FutureTask的对象futureTask作为Thread的参数来创建线程,并启动
new Thread(futureTask, "我是Callable线程").start();
// 调用对象futureTask的get()方法获得返回值,需要处理抛出的异常
String result = futureTask.get();
System.out.println("Callable线程执行结果:" + result);
}
}
补充使用Callbale方式的好处:
- call()方法有返回值。