概念图
什么是Java多线程
Java多线程是指在Java程序中同时运行多个线程的能力。每个线程都是程序中一个独立执行的路径。Java通过Thread类和Runnable接口来创建和管理线程,使得开发者可以编写并发执行的程序来提高效率。
为什么需要用到多线程
提高程序开发效率(一句话就是)
- 提高程序性能:多线程可以在多核CPU上并行处理任务,可以显著提高程序的执行效率和响应速度。
- 提升用户体验:在用户界面程序中,使用多线程可以使得后台任务(如文件下载或数据加载)在不阻塞主线程的情况下运行,从而确保界面始终响应用户的操作。
- 资源利用:通过多个线程可以更有效地利用系统资源,例如在I/O操作(如网络请求、文件读写)较慢时,其他线程可以继续执行其他任务。
- 简化复杂任务:将复杂的任务拆分成多个子任务并行处理,可以提高代码的可读性和可维护性。
多线程的作用
- 并行处理:通过并行执行多个任务来加速整体处理速度,特别是在处理大量数据或计算密集型任务时。
- 异步处理:允许程序在等待某些操作完成时(例如等待网络响应或文件I/O)做其他工作,避免阻塞主线程。
- 资源共享:多个线程可以共享程序内的资源,如内存、数据结构等,从而可以高效地处理共享数据。
- 任务调度:通过使用线程池等机制,可以有效地管理和调度大量短小的任务,提高资源的使用率。
使用多线程一定提高效率吗
使用多线程并不一定会提高效率,具体原因如下:
- 上下文切换开销:线程之间的切换需要操作系统进行上下文切换【从执行该线程到切换导执行另外的线程】,这会产生一定的性能开销。在某些情况下,频繁的上下文切换反而会降低程序的整体效率。(单核cpu)
- 竞争资源:多个线程同时访问共享资源时,可能会发生竞争,导致等待和阻塞,这会拖慢程序执行速度。
- 死锁:如果线程之间相互等待资源,就会导致死锁,整个程序可能会停止运行,造成效率下降。
- 复杂性增加:多线程增加了程序的复杂性,容易引入错误,如数据竞争和同步问题。这会导致难以调试和维护,间接影响效率。
- I/O密集型任务:对于大多数I/O密集型任务(如文件操作、网络请求等),使用单线程和异步编程可能会比多线程更有效,因为I/O操作通常会导致线程阻塞。
- 过多线程的负担:创建和维护太多线程也会占用系统资源,特别是在低配置的计算机上,可能导致性能下降。
因此,在决定是否使用多线程时,需要根据具体的应用场景、任务性质及系统资源进行综合考虑。合理地使用多线程可以提高性能,但不当的使用可能导致效率下降。比如:生产环境开启几百上千个线程,而服务器核数为8/16/32核的话 这么多线程都会在这些cpu上做上下文切换。
线程创建方式
继承Thread类创建线程
public class ThreadTest extends Thread{
// 线程执行的代码 执行完毕线程死亡
@Override
public void run() {
System.out.println("我是子线程"+Thread.currentThread().getName());
try {
Thread.sleep(5000); // 线程休眠5s 阻塞
} catch (Exception e){
System.out.println("阻塞错误" + e);
}
System.out.println("子线程执行完毕");
}
public static void main(String[] args) {
// 我是主线程=main
System.out.println("我是主线程="+ Thread.currentThread().getName());
// 调用 start() 就绪状态 等待cpu调度 线程从就绪到运行状态
new ThreadTest().start(); // 我是子线程Thread-0
new ThreadTest().start(); // 我是子线程Thread-1
System.out.println("主线程执行完毕");
}
}
实现Runnable接口创建线程
public class RunnableTest implements Runnable{
@Override
public void run(){
System.out.println("我是子线程RunnableTest"+Thread.currentThread().getName());
try {
Thread.sleep(5000); // 线程休眠5s 阻塞
} catch (Exception e){
System.out.println("阻塞错误" + e);
}
System.out.println("子线程执行完毕");
}
public static void main(String[] args) {
// 我是主线程=main
System.out.println("我是主线程="+ Thread.currentThread().getName());
// 我是子线程RunnableTestThread-0
new Thread(new RunnableTest()).start();
// 我是子线程RunnableTestThread-1
new Thread(new RunnableTest()).start();
}
}
使用Callable和Future创建线程
import java.util.concurrent.Callable;
// 手动实现 接口
public class ThreadCallable implements Callable<Integer> {
/*
* Callable接口中只有一个call方法,该方法有返回值,并且可以抛出异常
* 线程执行完毕之后,会将call方法返回值作为线程执行结果返回
* */
@Override
public Integer call() throws Exception{
try {
Thread.sleep(2000);
} catch(Exception e){
System.out.println("阻塞错误" + e);
}
// Thread-0返回结果666
System.out.println(Thread.currentThread().getName() + "返回结果666");
return 666;
}
}
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.locks.LockSupport;
public class Main {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// new出ThreadCallable这个类
ThreadCallable tc = new ThreadCallable();
FutureTask<Integer> ft = new FutureTask<Integer>(tc);
new Thread(ft).start();
Integer result = ft.get();
System.out.println(result + "子线程=" + Thread.currentThread().getName());
// Thread-0返回结果666 -> 子线程=main (主线程-> 子线程 阻塞主线程 -> 主线程)
// 底层实现
// LockSupport.park();
// LockSupport.unpark();
}
}
线程池创建线程
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println("我是子线程:"+Thread.currentThread().getName());
// 我是子线程:pool-1-thread-1
// 我是子线程:pool-1-thread-6
// 我是子线程:pool-1-thread-9
// 我是子线程:pool-1-thread-4
// 我是子线程:pool-1-thread-5
// 我是子线程:pool-1-thread-7
// 我是子线程:pool-1-thread-2
// 我是子线程:pool-1-thread-3
// 我是子线程:pool-1-thread-8
// 我是子线程:pool-1-thread-10
}
});
}
}
}
spring @Async异步注解创建线程
在Spring中,使用@Async注解可以轻松地将方法标记为异步执行,这样在调用该方法时,Spring会在新的线程中执行该方法,而不会阻塞调用者。
使用@Async的步骤
- 启用异步支持:在Spring配置类上添加
@EnableAsync注解,以启用异步方法的支持。 - 使用
@Async注解:在希望异步执行的方法上添加@Async注解。 - 配置Executor(可选) :可以通过配置自定义的线程池,以调整线程的数量和行为。
示例代码
以下是一个简单的示例,展示了如何在Spring中使用@Async注解进行异步操作。
1. 添加Spring依赖
确保您的项目中已添加Spring相关依赖,如果使用Maven,可以在pom.xml中添加:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
2. 创建一个异步服务类
创建一个服务类,其中包含异步方法:
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Service
public class AsyncService {
// @Async 一个注解
@Async
public void executeAsyncTask() {
System.out.println("异步任务开始: " + Thread.currentThread().getName());
try {
Thread.sleep(2000); // 模拟耗时操作
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("异步任务完成: " + Thread.currentThread().getName());
}
}
3. 启用异步支持
在应用的主类或配置类上启用异步支持:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
@EnableAsync
public class AsyncApplication {
public static void main(String[] args) {
SpringApplication.run(AsyncApplication.class, args);
}
}
4. 调用异步方法
在控制器或其他服务中调用异步方法:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class AsyncController {
@Autowired
private AsyncService asyncService;
@GetMapping("/startAsync")
public String startAsync() {
asyncService.executeAsyncTask();
return "异步任务已启动";
}
}
使用lambda表达式创建线程
public class LambdaThread {
public static void main(String[] args) {
// 使用Lambda表达式创建线程
Thread thread = new Thread(() -> {
// 线程要执行的任务
for (int i = 0; i < 5; i++) {
System.out.println("线程正在运行: " + i);
try {
Thread.sleep(500); // 线程休眠500毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
// 启动线程
thread.start();
// 主线程
for (int i = 0; i < 5; i++) {
System.out.println("主线程正在运行: " + i);
try {
Thread.sleep(1000); // 主线程休眠1s
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
使用匿名内部类的形式创建线程
public class InnerClass {
public static void main(String[] args) {
// 使用匿名内部类创建线程
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
// 线程要执行的任务
for (int i = 0; i < 5; i++) {
System.out.println("线程正在运行: " + i);
try {
Thread.sleep(500); // 线程休眠500毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
// 启动线程
thread.start();
// 主线程
for (int i = 0; i < 5; i++) {
System.out.println("主线程正在运行: " + i);
try {
Thread.sleep(700); // 主线程休眠700毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
好了创建线程的几种方式都在这里了~慢慢学吧。。。