Java多线程基础(一)

203 阅读7分钟

概念图

java多线程.png

什么是Java多线程

Java多线程是指在Java程序中同时运行多个线程的能力。每个线程都是程序中一个独立执行的路径。Java通过Thread类和Runnable接口来创建和管理线程,使得开发者可以编写并发执行的程序来提高效率。

为什么需要用到多线程

提高程序开发效率(一句话就是)

  1. 提高程序性能:多线程可以在多核CPU上并行处理任务,可以显著提高程序的执行效率和响应速度。
  2. 提升用户体验:在用户界面程序中,使用多线程可以使得后台任务(如文件下载或数据加载)在不阻塞主线程的情况下运行,从而确保界面始终响应用户的操作。
  3. 资源利用:通过多个线程可以更有效地利用系统资源,例如在I/O操作(如网络请求、文件读写)较慢时,其他线程可以继续执行其他任务。
  4. 简化复杂任务:将复杂的任务拆分成多个子任务并行处理,可以提高代码的可读性和可维护性。

多线程的作用

  1. 并行处理:通过并行执行多个任务来加速整体处理速度,特别是在处理大量数据或计算密集型任务时。
  2. 异步处理:允许程序在等待某些操作完成时(例如等待网络响应或文件I/O)做其他工作,避免阻塞主线程。
  3. 资源共享:多个线程可以共享程序内的资源,如内存、数据结构等,从而可以高效地处理共享数据。
  4. 任务调度:通过使用线程池等机制,可以有效地管理和调度大量短小的任务,提高资源的使用率。

使用多线程一定提高效率吗

使用多线程并不一定会提高效率,具体原因如下:

  1. 上下文切换开销:线程之间的切换需要操作系统进行上下文切换【从执行该线程到切换导执行另外的线程】,这会产生一定的性能开销。在某些情况下,频繁的上下文切换反而会降低程序的整体效率。(单核cpu)
  2. 竞争资源:多个线程同时访问共享资源时,可能会发生竞争,导致等待和阻塞,这会拖慢程序执行速度。
  3. 死锁:如果线程之间相互等待资源,就会导致死锁,整个程序可能会停止运行,造成效率下降。
  4. 复杂性增加:多线程增加了程序的复杂性,容易引入错误,如数据竞争和同步问题。这会导致难以调试和维护,间接影响效率。
  5. I/O密集型任务:对于大多数I/O密集型任务(如文件操作、网络请求等),使用单线程和异步编程可能会比多线程更有效,因为I/O操作通常会导致线程阻塞。
  6. 过多线程的负担:创建和维护太多线程也会占用系统资源,特别是在低配置的计算机上,可能导致性能下降。

因此,在决定是否使用多线程时,需要根据具体的应用场景、任务性质及系统资源进行综合考虑。合理地使用多线程可以提高性能,但不当的使用可能导致效率下降。比如:生产环境开启几百上千个线程,而服务器核数为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的步骤

  1. 启用异步支持:在Spring配置类上添加@EnableAsync注解,以启用异步方法的支持。
  2. 使用@Async注解:在希望异步执行的方法上添加@Async注解。
  3. 配置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();
            }
        }
    }
}

好了创建线程的几种方式都在这里了~慢慢学吧。。。