目录
1、多线程的三种实现方式
1.1、继承Thread类的方式进行实现
1.2、实现Runnable接口的方式进行实现
1.3、利用Callable接口和Future接口方式实现
1.4、三种实现方式的优缺点
2、多线程常用方法
1、多线程的三种实现方式
在main()方法中,你可以创建和启动额外的线程,这些线程称为子线程。子线程可以并行执行,与主线程同时进行。多线程的三种实现方式如下:
- 继承Thread类的方式进行实现
- 实现Runnable接口的方式进行实现
- 利用Callable接口和Future接口方式实现
1.1、继承Thread类的方式进行实现
java.lang.Thread是Java提供的线程类,继承了该类的就是一个线程类,所有的线程对象都必须是 Thread 类或其⼦类的实例。每个线程的作⽤是完成⼀定的任务,实际上就是执⾏⼀段程序流即⼀段顺序执⾏的代码,Java 使⽤线程执⾏体来代表这段程序流。主线程和分支线程是并发执行的,谁在前谁在后是随机的抢占式分配。Java中通过继承Thread类来创建并启动多线程的步骤如下:
1. 定义 Thread 类的⼦类,并重写该类的 run() ⽅法,该 run() ⽅法的⽅法体就代表了线程需要完成的任务,因此把 run() ⽅法称为线程执⾏体。
2. 创建 Thread ⼦类的实例,即创建了线程对象
3. 调⽤线程对象的 start() ⽅法来启动该线程
定义线程,getName()方法可以在线程启动时获取线程的名字
public class MyThread extends Thread{
@Override
public void run() {
//书写线程要执行代码
for (int i = 0; i < 15; i++) {
System.out.println(getName() + " Holle world");
}
}
}
创建线程对象,并启动线程,t1.setName("线程1"); t2.setName("线程2");可以给线程对象命名
public class ThreadDemo {
public static void main(String[] args) {
/*多线程的第一种启动方式:
1.自己定义一个类继承Thread
2.重写run方法
3.创建子类的对象,并启动线程
*/
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.setName("线程1");
t2.setName("线程2");
t1.start();
t2.start();
}
}
运行结果
1.2、实现Runnable接口的方式进行实现
采⽤ java.lang.Runnable 实现也是⾮常常⻅的⼀种,只需要重写 run ⽅法即可。
步骤如下:
1. 定义 Runnable 接⼝的实现类,并重写该接⼝的 run() ⽅法,该 run() ⽅法的⽅法体同样是该线程的线程执⾏体。
2. 创建 Runnable 实现类的实例,并以此实例作为 Thread 的 target 来创建 Thread 对象,该 Thread 对象才是 真正的线程对象。
3. 调⽤线程对象的 start() ⽅法来启动线程。
定义任务类,要实现Runnable接口,实现接口中的 run()方法
public class MyRun implements Runnable {
@Override
public void run() {
//书写线程要执行代码
for (int i = 0; i < 15; i++) {
//currentThread()方法获取到当前线程的对象
/*Thread t = Thread.currentThread();
System.out.println(t.getName()+"HelloWorld!");
*/
//把上面的两行代码改成链式编程的写法
System.out.println(Thread.currentThread().getName() + " Holle world");
}
}
}
创建任务的对象,创建线程对象,并且把任务传递给线程对象,然后开启线程
public class ThreadDemo {
public static void main(String[] args) {
/*多线程的第二种启动方式:1.自己定义一个类实现Runnable接口
2.重写里面的run方法
3.创建自己的类的对象
4.创建一个Thread类的对象,并开启线程
*/
//创建MyRun的对象
// 表示多线程要执行的任务
MyRun mr = new MyRun();
//创建线程对象,并且把任务传递给线程对象
Thread t1 = new Thread(mr);
Thread t2 = new Thread(mr);
//给线程设置名字
t1.setName("线程1");
t2.setName("线程2");
//开启线程
t1.start();
t2.start();
}
}
运行结果
1.3、利用Callable接口和Future接口方式实现
Callable接口是Runable接口的增强版。同样用call()方法作为线程的执行体,增强了之前的run()方法。因为call方法可以有返回值,也可以声明抛出异常。
Future接口用来代表Callable接口里的call()方法的返回值,FutureTask类是Future接口的一个实现类,FutureTask类实现了RunnableFuture接口,而RunnnableFuture接口继承了Runnable和Future接口,所以说FutureTask是一个提供异步计算的结果的任务。
步骤如下:
1.创建一个类MyCallable实现Callable接口
2.重写call (是有返回值的,表示多线程运行的结果)
3.创建MyCallable的对象(表示多线程要执行的任务)
4.创建FutureTask的对象(作用管理多线程运行的结果)
5.创建Thread类的对象,并启动(表示线程)
定义任务类,要实现Callable接口,实现接口中的 call() 方法
public class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int result = 0;
System.out.println(Thread.currentThread().getName() + " Holle world");
for (int i = 0; i < 15; i++) {
result += i;
}
return result;
}
}
创建FutureTask的对象,创建Thread类的对象,并启动
public class ThreadDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
/*多线程的第三种实现方式:
特点:可以获取到多线程运行的结果
*/
//创建MyCallable的对象(表示多线程要执行的任务)
MyCallable mc = new MyCallable();
//创建FutureTask的对象(作用管理多线程运行的结果)
FutureTask<Integer> fu = new FutureTask<>(mc);
FutureTask<Integer> fu1 = new FutureTask<>(mc);
//创建线程的对象
Thread t1 = new Thread(fu);
Thread t2 = new Thread(fu1);
t1.setName("线程1");
t2.setName("线程2");
//启动线程
t1.start();
t2.start();
//获取多线程运行的结果
Integer result1 = fu.get();
Integer result2 = fu1.get();
System.out.println(result1 + " : " + result2);
}
}
1.4、三种实现方式的优缺点
方式 | 优点 | 缺点 |
继承Thread类 | 编程比较简单,可以直接使用Thread类中的方法 | 可以扩展性较差,不能再继承其他的类 |
实现Runnable接口 | 扩展性强,实现该接口的同时还可以继承其他的类 | 编程相对复杂,不能直接使用Thread类中的方法 |
实现ca1lable接口 |
2、多线程常用方法
方法名称 | 说明 |
String getName() | 返回此线程的名称 |
int getId() | 返回此线程的ID |
void setName(String name) | 设置线程的名字(构造方法也可以设置名字) |
static Thread currentThread( ) | 获取当前线程的对象 |
static void sleep(long time) | 让线程休眠指定的时间,单位为毫秒 |
setPriority(int newPriority) | 设置线程的优先级 |
final int getPriority( ) | 获取线程的优先级 |
final void setDaemon(boolean on) | 设置为守护线程 |
public static void yield( ) | 出让线程/礼让线程 |
public static void join( ) | 插入线程/插队线程 |
常用方法注意点:
String getName() 返回此线程的名称
void setName(string name) 设置线程的名字(构造方法也可以设置名字)
1、如果我们没有给线程设置名字,线程也是有默认的名字的,格式是Thread-X(X序号,从0开始的)
2、如果我们要给线程设置名字,可以用set方法进行设置,也可以构造方法设置
static Thread currentThread() 获取当前线程的对象
当IVM虚拟机启动之后,会自动的启动多条线程,其中有一条线程就叫做main线程,他的作用就是去调用main方法,并执行里面的代码,在以前,我们写的所有的代码,其实都是运行在main线程当中
static void sleep(long time) 让线程休眠指定的时间,单位为毫秒
1、哪条线程执行到这个方法,那么哪条线程就会在这里停留对应的时间
2、方法的参数:就表示睡眠的时间,单位毫秒,1 秒=1000毫秒
3、当时间到了之后,线程会自动的醒来,抢占到CUP后,继续执行下面的其他代码
注意:Java中的线程调度采用的是抢占式的,线程的优先级从1到10,线程的优先级越低抢占CUP的概率就越低,线程的优先级越高抢占CUP的概率就越高,不是说线程的优先级越高就一定能抢占到CUP的,这是一个概率问题,并不是绝对的
自定义线程两个线程
//自定义线程MyThread1
public class MyThread1 extends Thread{
@Override
public void run() {
//书写线程要执行代码
for (int i = 0; i < 15; i++) {
System.out.println(getName() +"@"+ i);
}
}
}
//自定义线程MyThread2
public class MyThread2 extends Thread {
@Override
public void run() {
//书写线程要执行代码
for (int i = 0; i < 70; i++) {
System.out.println(getName() +"@"+ i);
}
}
}
final void setDaemon(boolean on)方法演示
守护线程的应用场景,假设有两个人通过QQ在聊天,并且有文件在传输,这时候就会有两个线程,分别是:聊天(线程1)和传输文件(线程2),当聊天结束,即线程1被关闭了,传输文件自然也就要关闭了,所以线程2要被设置成守护线程
public class ThreadDemo1 {
public static void main(String[] args) {
/*
* final void setDaemon(boolean on) 设置为守护线程
* 提示:当其他的非守护线程执行完毕之后,守护线程会陆续结束
* 通俗易懂:当女神线程结束了,那么备胎线程也没有存在的必要了
*/
MyThread1 t1 = new MyThread1();
MyThread2 t2 = new MyThread2();
t1.setName("主线程");
t2.setName("守护线程");
//把第二个线程设置为守护线程(备胎线程)
t2.setDaemon(true);
//启动线程
t1.start();
t2.start();
}
}
从运行结果可看出,主线程执行完毕之后,守护线程就陆续结束了
java中线程的执行过程
Java中的主线程是程序的入口点,通常是在main()方法中定义。当程序启动时,主线程会从main()方法开始执行。当主线程遇到创建和启动子线程的代码时,它会创建一个新的线程对象,并调用该线程对象的start()方法,以启动新的线程。子线程在自己的run()方法中定义要执行的任务。
主线程和子线程之间的执行顺序取决于调度器的策略。调度器负责分配CPU时间给每个线程,以便它们能够交替执行。
通常情况下,主线程会先执行main()方法中的代码,然后创建和启动子线程。子线程的执行顺序可能是不确定的,取决于操作系统和Java虚拟机的实现。主线程和子线程可以同时执行,互不干扰。
需要注意的是,如果主线程在子线程之前结束,那么子线程可能无法执行完毕。为了确保子线程能够完成任务,可以使用join()方法,在主线程中调用该方法等待子线程执行完毕。
推荐:
【Java多线程】多线程的三种实现方式和多线程常用方法-CSDN博客https://blog.csdn.net/m0_65277261/article/details/136961604【Java多线程】多线程的三种实现方式和多线程常用方法-CSDN博客https://blog.csdn.net/m0_65277261/article/details/136961604【java基础】异常处理机制-CSDN博客https://blog.csdn.net/m0_65277261/article/details/136581375?spm=1001.2014.3001.5501