• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 众包
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
自由的代价是孤独丶
博客园    首页    新随笔    联系   管理    订阅  订阅

多线程核心知识

1. 线程的创建方式

  1. 继承Thread

    • 方法:继承 java.lang.Thread 类,重写 run() 方法。
    • 启动:创建子类实例,调用其 start() 方法。
    • 特点:简单,但Java单继承限制了扩展性。
    class MyThread extends Thread {
        @Override
        public void run() {
            System.out.println("线程执行");
        }
    }
    // 启动
    new MyThread().start();
    



  1. 实现Runnable

    • 方法:实现 java.lang.Runnable 接口,实现 run() 方法。
    • 启动:将 Runnable 实例作为参数传给 Thread 构造器,再调用 Thread 实例的 start() 方法。
    • 特点:可继承其他类。
    class MyRunnable implements Runnable {
        @Override
        public void run() {
            System.out.println("线程执行");
        }
    }
    // 启动
    Thread thread = new Thread(new MyRunnable());
    thread.start();
    



  1. 实现 Callable 接口

    • 方法:实现 java.util.concurrent.Callable<V> 接口,实现 call() 方法(可抛出异常、有返回值)。
    • 启动:将 Callable 实例提交给线程池(如 ExecutorService),或封装为 FutureTask 再传给 Thread。
    • 特点:可获取异步执行结果(通过 Future / FutureTask),可捕获执行过程中的异常。
    class MyCallable implements Callable<String> {
        @Override
        public String call() throws Exception {
            return "执行结果";
        }
    }
    // 使用FutureTask启动
    FutureTask<String> task = new FutureTask<>(new MyCallable());
    new Thread(task).start();
    String result = task.get(); // 获取返回值
    

     Future与FutureTask简要说明:
       Future它是一个接口,位于java.util.concurrent包下。用于表示异步处理的结果。它提供了一组方法来判断异步处理是否完成、获取异步处理结果,以及取消异步处理等。‌

       FutureTask是Future的其中一个实现类,且没有额外提供新方法。CompletableFuture也是实现类之一(Java 8+)提供更强大的异步编程能力。额外提供了很多新方法。

 // Future常用方法
 public interface Future<V> {
     boolean cancel(boolean mayInterruptIfRunning);  // 取消任务
     boolean isCancelled();                          // 是否被取消
     boolean isDone();                               // 是否完成(正常/异常/取消)
     V get() throws InterruptedException, ExecutionException;      // 阻塞获取结果(阻塞直到计算完成)
     V get(long timeout, TimeUnit unit) throws ...; // 超时获取结果
 }
 // FutureTask构造方法
 public FutureTask(Callable<V> callable)
 public FutureTask(Runnable runnable, V result)   // 参数2:预设的返回结果



  1. 使用线程池

    • 方法:通过 Executors 工具类或 ThreadPoolExecutor 创建线程池,提交 Runnable 或 Callable 任务。
    • 启动:调用线程池的 execute()(提交 Runnable)或 submit()(提交 Callable/Runnable)方法。
    • 特点:实际开发中最常用,利于线程复用、管理和控制并发数。
    // 不推荐是用Executors来创建,阿里手册有说明这个情况。
    // Executors创建线程池示例:
    ExecutorService executor = Executors.newFixedThreadPool(5);
    executor.execute(() -> System.out.println("Runnable任务")); // 提交Runnable
    Future<String> future = executor.submit(new MyCallable()); // 提交Callable
    

     execute()与submit()简要说明:具体说明用到时再自行查阅

image







2. 线程安全

核心思想:保证多线程环境下共享数据的一致性和操作正确性


  1. 同步方法
public class Counter {
    private int count = 0;
    
    // 实例同步方法 - 锁this对象
    public synchronized void increment() {
        count++;
        System.out.println(Thread.currentThread().getName() + ": " + count);
    }
    
    // 多个同步方法互斥:线程A执行increment()时,线程B无法执行decrement()
    public synchronized void decrement() {
        count--;
    }
}



  1. 同步代码块
public class FineGrainedLock {
    private int count1 = 0;
    private int count2 = 0;
    
    // 使用不同锁对象,提高并发度
    private final Object lock1 = new Object();
    private final Object lock2 = new Object();
    
    public void increment1() {
        synchronized(lock1) {  // 只锁count1相关操作
            count1++;
        }
    }
    
    public void increment2() {
        synchronized(lock2) {  // 只锁count2相关操作
            count2++;
        }
        // increment1和increment2可并发执行
    }
    
    public void incrementBoth() {
        synchronized(lock1) {  // 需要获取两把锁
            synchronized(lock2) {
                count1++;
                count2++;
            }
        }
        // 注意:可能产生死锁,需要固定锁顺序
    }
}



  1. Lock锁
public class LockExample {
    private final Lock lock = new ReentrantLock();
    private int count = 0;
    
    public void increment() {
        lock.lock();          // 手动获取锁
        try {
            count++;
            // 可能抛出异常的代码放在try块
        } finally {
            lock.unlock();    // 必须finally中释放
        }
    }
    
    // 错误示例:忘记unlock()
    public void badIncrement() {
        lock.lock();
        count++;
        // 如果这里抛出异常,锁永远不会释放!
        // 正确做法:用try-finally包裹
    }
}

image







3. 线程池

核心思想:管理一组工作线程的池化技术,复用线程,避免频繁创建/销毁开销。

Executor (接口)
    ↑
ExecutorService (接口)
    ↑
AbstractExecutorService (抽象类)
    ↑
ThreadPoolExecutor (核心实现类)
    ↑
ScheduledThreadPoolExecutor (定时任务)

// 核心参数(ThreadPoolExecutor 7大参数)
ThreadPoolExecutor(
    int corePoolSize,      // 核心线程数—————————— 根据CPU密集型/IO密集型设置
    int maximumPoolSize,   // 最大线程数 —————————— CPU密集型:N+1、IO密集型:2N
    long keepAliveTime,    // 空闲线程存活时间
    TimeUnit unit,         // 空闲线程存活时间的时间单位
    BlockingQueue<Runnable> workQueue,  // 任务队列 —————————— 常用ArrayBlockingQueue
                                            // LinkedBlockingQueue        无界队列(默认Integer.MAX_VALUE),任务可能无限堆积,导致OOM
                                            // SynchronousQueue           同步队列,不存储元素,直接传递              
                                            // ArrayBlockingQueue         有界队列,需指定容量              
                                            // PriorityBlockingQueue      优先级队列                    

    ThreadFactory threadFactory,        // 创建线程的工厂

    RejectedExecutionHandler handler    // 拒绝策略(队列满且线程达最大时的拒绝策略),拒绝策略是系统的最后防线,应保证系统不会因为任务被拒绝而崩溃,并提供降级方案。
                                        // 有4种内置策略,一般实际开发时会自定义拒绝策略(实现RejectedExecutionHandler接口,重写rejectedExecution方法)
                                            // AbortPolicy             中止策略/抛出异常策略,直接抛出RejectedExecutionException 
                                            // CallerRunsPolicy        调用者运行策略,由提交任务的线程自己执行该任务                  
                                            // DiscardPolicy           丢弃策略,静默丢弃被拒绝的任务,不做任何处理                     
                                            // DiscardOldestPolicy     丢弃最旧策略,丢弃队列头部任务(最先提交的任务),重试提交                
)



// 自定义线程池示例:
ThreadPoolExecutor customPool = new ThreadPoolExecutor(
    5,  // corePoolSize
    10, // maximumPoolSize
    60, // keepAliveTime
    TimeUnit.SECONDS,
    new ArrayBlockingQueue<>(100),  // 有界队列,防止OOM
    new ThreadFactory() {           // 自定义线程工厂
        private AtomicInteger count = new AtomicInteger(1);
        @Override
        public Thread newThread(Runnable r) {
            Thread thread = new Thread(r);
            thread.setName("自定义线程-" + count.getAndIncrement());
            thread.setDaemon(false); // 非守护线程
            return thread;
        }
    },
    new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);



  1. 线程池工作原理
ThreadPoolExecutor executor = new ThreadPoolExecutor(
    2,  // 核心线程数 = 2
    4,  // 最大线程数 = 4
    60, TimeUnit.SECONDS,
    new ArrayBlockingQueue<>(2)  // 队列容量 = 2
);

// 场景模拟:
1. 提交任务1 → 创建核心线程1执行 ✓
2. 提交任务2 → 创建核心线程2执行 ✓
3. 提交任务3 → 核心线程已满,放入队列 ✓
4. 提交任务4 → 放入队列 ✓
5. 提交任务5 → 队列已满,创建非核心线程3执行 ✓
6. 提交任务6 → 创建非核心线程4执行 ✓
7. 提交任务7 → 线程数已达最大(4),队列已满(2),执行拒绝策略 ✗



  1. SpringBoot整合线程池
    前言:因为我们现在一般都使用springboot,而springboot提供了一个更强大的类(ThreadPoolTaskExecutor)去帮助我们整合线程池。
    好处:如果你使用ThreadPoolTaskExecutor这种方式的话,能使用其提供的注解@Async。

// 1. 启动类添加@EnableAsync
@SpringBootApplication
@EnableAsync  // 启用异步支持
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

// 2. 创建线程池配置类。配置线程池(可选,不配置使用默认)
@Configuration
public class AsyncConfig {
    
    @Bean("taskExecutor")
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);        // 核心线程数
        executor.setMaxPoolSize(10);        // 最大线程数
        executor.setQueueCapacity(100);     // 队列容量
        executor.setKeepAliveSeconds(60);   // 线程空闲时间
        executor.setThreadNamePrefix("Async-"); // 线程名前缀
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }
}

// 3. 使用线程池
@Service
public class EmailServiceImpl implements EmailService{
    
    // 将需要异步执行的方法用 @Async 标记
    @Async("taskExecutor")
    @Override
    public void sendSimpleEmail(String to, String content) {
        // 模拟发送邮件耗时
        try {
            Thread.sleep(3000);
            System.out.println("发送邮件给: " + to + ", 内容: " + content);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

// 4. 调用方
@RestController
@RequestMapping("/email")
public class EmailController {
    
    @Autowired
    private EmailService emailService;
    
    @GetMapping("/send")
    public String sendEmail() {
        // 立即返回,不阻塞
        emailService.sendSimpleEmail("user@example.com", "测试邮件");
        return "邮件发送请求已接收";
    }
}



  1. 线程池中是否能支持事务
    结论:线程池本身不提供事务管理,多线程中的事务处理需要额外设计。
    最佳实践:尽量不在事务中开启多线程。
// 问题:事务方法在子线程中不生效
@Transactional
public void parentMethod() {
    // 主线程事务
    userDao.update(user);
    
    // 子线程:事务会失效!
    executor.execute(() -> {
        orderDao.createOrder(order); 
    });
}



  1. 线程池中如何保证事务
方案一:利用completable异步编排实现






springboot整合线程池(ThreadPoolTaskExecutor),利用注解@Async开启异步处理。有事务的情况(用到的时候自行百度,有很多解决方案,以下方案没测试)

// 问题根源展示
@Service
public class OrderService {
    
    @Async  // 新线程执行
    @Transactional  // 期望有事务,但实际上...
    public void createOrder(Order order) {
        // 在多线程中:@Transactional注解失效!
        // 原因:事务基于ThreadLocal,而新线程无法获取父线程的事务上下文
        orderDao.save(order);
        inventoryDao.deduct(order.getProductId(), order.getQuantity());
        // 如果这里异常,不会回滚!
    }
}

image
PS:前两种方案都无法直接实现跨线程的事务原子性。必须通过业务设计(发生异常后,写补偿逻辑)来实现原子性。后两种推荐最后一种


image







4. 异步编排

CompletableFuture是 Java 8 引入的异步编程工具, 实现了 Future 和 CompletionStage 接口,提供了更强大的异步编程能力,支持链式调用、组合多个异步任务、异常处理等功能。

posted @ 2026-01-24 17:48  &emsp;不将就鸭  阅读(1)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3