简述多线程概念
1.什么是多线程?
有了多线程,我们可以让程序同事做多件事情。
2.多线程的作用?
提高效率
3.多线程的应用场景
只要你想让多个事情同事运行就需要用到多线程。
比如软件中耗时操作、所有的软件聊天、所有的服务器。
并发和并行?
并发:在同一时刻,有多个指令在单个cpu上交替执行
在玩游戏的时候,你的右手一会抽烟、一会喝可乐、一会按鼠标,来回操作
此时,右手相当于CPU、鼠标、可乐、抽烟相当于线程n
如上图 线程一会执行右边的指令 一会执行右边的指令
并行:同一时刻,有多个指令在多个cpu上同时执行
如左图,两个线程同时处理连个指令
一般CPU有分为n和m线程,m的数量代表了他能同时处理的数量,当同时处理的线程数达到最大时,此时就会进行交替处理。
多线程的实现方式
线程(Thread)是程序中的执行线程。Java 虚拟机允许应用程序并发地运行多个执行线程。 每个线程都有一个优先级,高优先级线程的执行优先于低优先级线程。每个线程都可以或者不可以标记一个守护程序。当某人线程中的运行代码创建一个新的Thread对象时,该新线程的初始优先级被设定为创建线程的优先级,并且当且仅当创建线程是守护线程时,新线程才是守护程序。
当java虚拟机启动时,通常会有当个非守护线程(它通常会调用某个指定类的main方法)。java虚拟机会继续执行线程,知道下列任一情况出现为止。
·调用了runtime类的exit方法,并且安全管理器允许退出操作发生
·非守护线程的所有线程都已停止运行,无论是通过从对run方法的调用中返回,还是通过抛出一个传播到run方法之外的异常。
第一种 继承Thread类 重写run方法:
1.定义一个类,继承了Thread(本质上是实现了runnable接口),重写run方法。
2.通过Thread.start()方法(且一个线程只允许调佣一次)启动线程。
·Thread.start方法是native方法(只有方法名,没有方法体)
·继承Thread类无法在继承其他业务类。
·此方法无法获取返回值。
package com.ruoyi.web.controller.thread;
/**
* 多线程四种处理方式 -- 第一种 继承Thread类 重写run方法
*
* @author zhuang.bq
* @create 2024/5/13 9:13
* @desc
**/
public class MyThread1 extends Thread {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
String threadName = MyThread1.currentThread().getName();
System.out.println(threadName + ".start");
}
}
public static void main(String[] args) {
MyThread1 thread1 = new MyThread1();
thread1.setName("线程1");
thread1.start();
MyThread1 thread2 = new MyThread1();
thread2.setName("线程2");
thread2.start();
}
}
从执行的结果可以看到线程在交替运行。
第二种 实现runnable接口方式实现
1.定义一个类,实现Runable接口(避免单继承的局限性),重写run方法(子类负责业务逻辑,thread负责资源调度及线程创建辅助处理业务)
2.创建类对象,创建一个Thread类对象并开启线程
3.同上,此方法无法获取返回值。
package com.ruoyi.web.controller.thread;
/**
* 实现runnable接口方式实现
*
* @author zhuang.bq
* @create 2024/5/13 15:57
* @desc
**/
public class MyRun1 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
String threadName = Thread.currentThread().getName();
System.out.println(threadName + ".run");
}
}
public static void main(String[] args) {
MyRun1 myRun1 = new MyRun1();//创建run对象 表示需要执行的任务
Thread t1 = new Thread(myRun1);//创建线程对象,并将自定义线程丢进去
t1.setName("线程1");
t1.start();//开启线程
Thread t2 = new Thread(myRun1);
t2.setName("线程2");
t2.start();
}
}
从执行的结果可以看到线程在交替运行。
第三种 利用callable接口和Future接口方式实现
1.定义一个类实现Callable接口,并重写call方法(call方法有返回值<T>)
2.创建Callable实现类的对象
3.创建FutureTask对象(作用管理多线程运行结果),并将Callable对象传递给FutureTask
4.创建Thread对象,将FutureTask对象传递给Thread,并启动线程Thread.start
5.调用FutrueTask对象的get()方法获取返回结果
package com.ruoyi.web.controller.thread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* 利用callable接口和Future接口方式实现
*
* @author zhuang.bq
* @create 2024/5/13 16:09
* @desc
**/
public class MyCallAble implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName()+".call");
int sum = 0;
for (int i = 0; i < 1000; i++) {
sum = sum + i;
}
return sum;
}
public static void main(String[] args) {
MyCallAble myCallAble = new MyCallAble();
FutureTask<Integer> task = new FutureTask(myCallAble);
Thread t1 = new Thread(task);
t1.setName("线程1");
t1.start();//开启线程
try {
Integer ret = task.get();
//获取多线程运行的结果
System.out.println("call.ret:"+ret);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
FutureTask<Integer> task2 = new FutureTask(myCallAble);
Thread t2 = new Thread(task2);
t2.setName("线程2");
t2.start();//开启线程
try {
Integer ret = task2.get();
//获取多线程运行的结果
System.out.println("call.ret:"+ret);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
第四种 通过线程池启动多线程
通过Executor的工具类可以创建三种类型的普通线程池(固定大小线程池、缓存线程池、单线程池)
单线程池:SingleThreadPoolExecutor,用于需要固定执行顺序的业务场景
package com.ruoyi.web.controller.thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 单线程池
*
* @author zhuang.bq
* @create 2024/5/13 17:17
* @desc
**/
public class MyExecutor1 {
public static void main(String[] args) {
ExecutorService ex = Executors.newSingleThreadExecutor();
for (int i = 0; i < 3; i++) {
ex.submit(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 5; j++) {
System.out.println(Thread.currentThread().getName()+j);
}
}
});
}
ex.shutdown();
}
}
固定大小线程池:FixThreadPool,根据服务器资源来固定设置线程池大小,用于负载比较重的服务器。
package com.ruoyi.web.controller.thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 单线程池
*
* @author zhuang.bq
* @create 2024/5/13 17:17
* @desc
**/
public class MyExecutor2 {
public static void main(String[] args) {
ExecutorService ex = Executors.newFixedThreadPool(5);
for (int i = 0; i < 3; i++) {
ex.submit(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 5; j++) {
System.out.println(Thread.currentThread().getName() + j);
}
}
});
}
ex.shutdown();
}
}
缓存线程池:CashedThreadPool,当提交任务速度高于线程池中的任务处理速度时,缓存线程池会不断的创建线程, 适用于提交短期的异步小程序,以及负载较轻的服务器
package com.ruoyi.web.controller.thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 单线程池
*
* @author zhuang.bq
* @create 2024/5/13 17:17
* @desc
**/
public class MyExecutor3 {
public static void main(String[] args) {
ExecutorService ex = Executors.newCachedThreadPool();
for (int i = 0; i < 3; i++) {
ex.submit(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 5; j++) {
System.out.println(Thread.currentThread().getName() +" "+ j);
}
}
});
}
ex.shutdown();
}
}
以上四种方式优缺点: