百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

SpringBoot异步处理@Async深度解析:从基础到高阶实战

haoteby 2025-05-26 15:53 6 浏览

一、异步编程基础概念

1.1 同步 vs 异步

特性

同步

异步

执行方式

顺序执行,阻塞调用

非阻塞,调用后立即返回

线程使用

单线程完成所有任务

多线程并行处理

响应性

较差,需等待前任务完成

较好,可立即响应新请求

复杂度

简单直观

较复杂,需处理线程安全

适用场景

简单流程,短时间任务

IO密集型,长时间任务

通俗理解:同步就像在银行柜台排队办理业务,必须等前面的人办完才能轮到你;异步则像取号后可以坐着玩手机,等叫号时再去办理。

1.2 为什么要使用异步

  • 提高吞吐量:服务器能同时处理更多请求
  • 增强用户体验:避免用户长时间等待
  • 资源优化:合理利用系统资源,避免阻塞主线程
  • 解耦:将耗时操作与主流程分离

1.3 Java中的异步编程方式

// 1. 传统Thread方式
new Thread(() -> {
    // 异步任务
}).start();

// 2. Future模式
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> future = executor.submit(() -> {
    Thread.sleep(1000);
    return "Result";
});

// 3. CompletableFuture (Java8+)
CompletableFuture.supplyAsync(() -> {
    // 异步任务
    return "Result";
}).thenAccept(result -> {
    // 处理结果
});

// 4. Spring @Async (本文重点)
@Async
public void asyncMethod() {
    // 异步方法体
}

二、@Async基础使用

2.1 启用@Async支持

步骤1:在Spring Boot主类或配置类上添加@EnableAsync

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

步骤2:创建异步服务类

@Service
public class EmailService {
    
    // 无返回值异步方法
    @Async
    public void sendEmail(String to, String content) {
        // 模拟邮件发送耗时
        try {
            Thread.sleep(3000);
            System.out.println("邮件已发送至: " + to + ", 内容: " + content);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    // 有返回值异步方法
    @Async
    public Future<String> sendEmailWithResult(String to, String content) {
        try {
            Thread.sleep(3000);
            String result = "邮件已发送至: " + to;
            return new AsyncResult<>(result);
        } catch (InterruptedException e) {
            return new AsyncResult<>("发送失败");
        }
    }
}

2.2 调用异步方法

@RestController
@RequestMapping("/api/email")
public class EmailController {
    
    @Autowired
    private EmailService emailService;
    
    @GetMapping("/send")
    public String sendEmail() {
        long start = System.currentTimeMillis();
        
        // 调用异步方法
        emailService.sendEmail("user@example.com", "您的订单已创建");
        
        long elapsed = System.currentTimeMillis() - start;
        return "请求已处理,耗时: " + elapsed + "ms"; // 立即返回,不会等待邮件发送完成
    }
    
    @GetMapping("/send-with-result")
    public String sendEmailWithResult() throws Exception {
        Future<String> future = emailService.sendEmailWithResult("user@example.com", "订单详情");
        
        // 可以在这里做其他事情
        
        // 当需要结果时(阻塞等待)
        String result = future.get();
        return "处理结果: " + result;
    }
}

2.3 @Async方法限制

  1. 必须public修饰:因为基于Spring AOP实现
  2. 同类调用无效this.asyncMethod()不会异步执行
  3. 返回值限制
  4. void
  5. Future及其子类(如AsyncResult)
  6. CompletableFuture (Spring 4.2+)
  7. ListenableFuture (Spring 4.2+)

三、线程池配置详解

3.1 默认线程池问题

Spring默认使用SimpleAsyncTaskExecutor,它不重用线程,每次调用都创建新线程,生产环境不推荐使用。

3.2 自定义线程池配置

方式1:配置类方式(推荐)

@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
    
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 核心线程数:线程池创建时初始化的线程数
        executor.setCorePoolSize(5);
        // 最大线程数:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程
        executor.setMaxPoolSize(10);
        // 缓冲队列:用来缓冲执行任务的队列
        executor.setQueueCapacity(50);
        // 线程名前缀
        executor.setThreadNamePrefix("Async-Executor-");
        // 线程池关闭时等待所有任务完成
        executor.setWaitForTasksToCompleteOnShutdown(true);
        // 等待时间
        executor.setAwaitTerminationSeconds(60);
        // 拒绝策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }
    
    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new CustomAsyncExceptionHandler();
    }
}

// 自定义异常处理器
public class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
    @Override
    public void handleUncaughtException(Throwable ex, Method method, Object... params) {
        System.err.println("异步任务异常 - 方法: " + method.getName());
        System.err.println("异常信息: " + ex.getMessage());
        // 这里可以添加自定义处理逻辑,如记录日志、发送告警等
    }
}

方式2:使用@Bean定义多个线程池

@Configuration
public class TaskExecutorConfig {
    
    @Bean(name = "emailExecutor")
    public Executor emailTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(3);
        executor.setMaxPoolSize(5);
        executor.setQueueCapacity(30);
        executor.setThreadNamePrefix("Email-Executor-");
        executor.initialize();
        return executor;
    }
    
    @Bean(name = "reportExecutor")
    public Executor reportTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);
        executor.setMaxPoolSize(4);
        executor.setQueueCapacity(20);
        executor.setThreadNamePrefix("Report-Executor-");
        executor.initialize();
        return executor;
    }
}

使用指定线程池:

@Async("emailExecutor")
public void sendEmail(String to) { /*...*/ }

@Async("reportExecutor")
public void generateReport() { /*...*/ }

3.3 线程池参数详解

参数名

说明

推荐设置建议

corePoolSize

核心线程数,即使空闲也不会被回收

CPU密集型:CPU核数+1 IO密集型:2*CPU核数

maxPoolSize

最大线程数,当队列满时才会创建新线程直到此值

建议为核心线程数的2-3倍

queueCapacity

任务队列容量,超过核心线程数的任务会进入队列

根据业务量调整,太大可能导致OOM

keepAliveSeconds

非核心线程空闲存活时间(秒)

60-300秒

threadNamePrefix

线程名前缀,便于监控和日志追踪

建议按业务命名,如"Order-Async-"

allowCoreThreadTimeOut

是否允许核心线程超时退出

默认false,长时间空闲应用可设为true

waitForTasksToCompleteOnShutdown

应用关闭时是否等待异步任务完成

生产环境建议true

awaitTerminationSeconds

等待任务完成的超时时间

根据业务最长执行时间设置

rejectedExecutionHandler

拒绝策略,当线程池和队列都满时的处理方式

根据业务需求选择

拒绝策略选项

  • AbortPolicy:默认,直接抛出RejectedExecutionException
  • CallerRunsPolicy:由调用者线程执行该任务
  • DiscardPolicy:直接丢弃任务
  • DiscardOldestPolicy:丢弃队列中最老的任务并重试

3.4 线程池监控

@Service
public class ThreadPoolMonitor {
    
    @Autowired
    private ThreadPoolTaskExecutor emailExecutor;
    
    @Scheduled(fixedRate = 5000)  // 每5秒监控一次
    public void monitor() {
        System.out.println("=== 线程池监控 ===");
        System.out.println("当前线程数: " + emailExecutor.getPoolSize());
        System.out.println("活跃线程数: " + emailExecutor.getActiveCount());
        System.out.println("完成任务数: " + emailExecutor.getCompletedTaskCount());
        System.out.println("队列剩余容量: " + emailExecutor.getThreadPoolExecutor().getQueue().remainingCapacity());
    }
}

四、@Async高级特性

4.1 返回值处理

1. Future模式

@Async
public Future<String> processData(String input) {
    // 模拟处理耗时
    try {
        Thread.sleep(2000);
        return new AsyncResult<>("处理完成: " + input.toUpperCase());
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        return new AsyncResult<>("处理中断");
    }
}

// 调用方
Future<String> future = service.processData("hello");
// 可以做其他事情...
String result = future.get(3, TimeUnit.SECONDS); // 带超时的等待

2. CompletableFuture (Java8+)

@Async
public CompletableFuture<String> fetchData(String param) {
    return CompletableFuture.supplyAsync(() -> {
        try {
            Thread.sleep(1000);
            return "Data for " + param;
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    });
}

// 链式调用
service.fetchData("user123")
       .thenApply(String::toUpperCase)
       .thenAccept(System.out::println)
       .exceptionally(ex -> {
           System.err.println("Error: " + ex.getMessage());
           return null;
       });

4.2 基于条件的异步执行

1. 使用Spring Expression Language (SpEL)

@Async("#{systemProperties['async.enabled'] ? 'emailExecutor' : 'syncExecutor'}")
public void conditionalAsync() {
    // 根据系统属性决定使用哪个执行器
}

2. 基于配置的开关

@Async
@ConditionalOnProperty(name = "app.async.enabled", havingValue = "true")
public void configBasedAsync() {
    // 当app.async.enabled=true时才异步执行
}

4.3 事务处理

异步方法与事务的特殊关系:

  1. 事务边界@Async方法会在新线程中执行,与原方法不在同一事务中
  2. 传播行为:需要在异步方法上单独声明@Transactional
@Async
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void asyncWithTransaction() {
    // 这个方法会在新事务中执行
    userRepository.save(new User("AsyncUser"));
    // 如果发生异常,只会回滚当前方法内的操作
}

4.4 组合异步操作

场景:需要等待多个异步任务全部完成

@Async
public CompletableFuture<String> fetchUserInfo(String userId) {
    // 模拟获取用户信息
    return CompletableFuture.completedFuture("UserInfo-" + userId);
}

@Async
public CompletableFuture<String> fetchOrderInfo(String userId) {
    // 模拟获取订单信息
    return CompletableFuture.completedFuture("OrderInfo-" + userId);
}

// 组合多个异步任务
public CompletableFuture<Void> combineAsyncTasks(String userId) {
    return CompletableFuture.allOf(
            fetchUserInfo(userId),
            fetchOrderInfo(userId)
    ).thenRun(() -> {
        // 所有任务完成后的处理
        System.out.println("所有异步任务已完成");
    });
}

五、异常处理机制

5.1 异常处理方式对比

处理方式

适用场景

优点

缺点

AsyncUncaughtExceptionHandler

处理void返回类型的异步方法异常

集中处理,统一管理

无法获取方法返回值

Future.get()

处理有返回值的异步方法异常

可以获取具体异常信息

需要手动调用get()

CompletableFuture.exceptionally

Java8+的优雅异常处理方式

链式调用,代码简洁

仅适用于CompletableFuture

5.2 实践示例

1. 全局异常处理器

public class GlobalAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
    private static final Logger logger = LoggerFactory.getLogger(GlobalAsyncExceptionHandler.class);
    
    @Override
    public void handleUncaughtException(Throwable ex, Method method, Object... params) {
        logger.error("异步任务异常 - 方法: {}, 参数: {}", method.getName(), Arrays.toString(params), ex);
        
        // 可以根据异常类型进行不同处理
        if (ex instanceof BusinessException) {
            // 业务异常处理
            sendAlert("业务异常警报: " + ex.getMessage());
        } else if (ex instanceof TimeoutException) {
            // 超时处理
            retryTask(method, params);
        }
    }
    
    private void sendAlert(String message) { /*...*/ }
    private void retryTask(Method method, Object... params) { /*...*/ }
}

2. Future方式的异常处理

@Async
public Future<String> asyncTaskWithException() {
    try {
        // 业务逻辑
        if (someCondition) {
            throw new BusinessException("业务异常");
        }
        return new AsyncResult<>("成功");
    } catch (BusinessException e) {
        return new AsyncResult<>("失败: " + e.getMessage());
    }
}

// 调用方处理
Future<String> future = service.asyncTaskWithException();
try {
    String result = future.get();
    if (result.startsWith("失败")) {
        // 处理失败情况
    }
} catch (ExecutionException e) {
    // 处理执行时异常
}

3. CompletableFuture的异常处理

@Async
public CompletableFuture<String> asyncProcess(String input) {
    return CompletableFuture.supplyAsync(() -> {
        if (input == null) {
            throw new IllegalArgumentException("输入不能为空");
        }
        return "处理结果: " + input.toUpperCase();
    });
}

// 调用方处理
service.asyncProcess(null)
       .exceptionally(ex -> {
           System.err.println("发生异常: " + ex.getMessage());
           return "默认返回值";
       })
       .thenAccept(result -> {
           System.out.println("最终结果: " + result);
       });

六、性能优化与最佳实践

6.1 性能优化建议

  1. 线程池参数调优
  2. 根据业务类型调整线程池大小
  3. 监控线程池状态动态调整参数
  4. 使用有界队列防止OOM
  5. 避免长时间阻塞
  6. 异步方法内避免同步阻塞操作
  7. 使用带超时的阻塞调用
  8. 资源清理
  9. 确保异步方法正确释放资源
  10. 使用try-with-resources管理资源
  11. 上下文传递
  12. 注意ThreadLocal变量在异步线程中的传递问题
  13. 使用TaskDecorator传递上下文
executor.setTaskDecorator(new ContextCopyingDecorator());

public class ContextCopyingDecorator implements TaskDecorator {
    @Override
    public Runnable decorate(Runnable runnable) {
        // 获取当前线程的上下文
        RequestAttributes context = RequestContextHolder.currentRequestAttributes();
        return () -> {
            try {
                // 在新线程中设置上下文
                RequestContextHolder.setRequestAttributes(context);
                runnable.run();
            } finally {
                RequestContextHolder.resetRequestAttributes();
            }
        };
    }
}

6.2 最佳实践清单

  1. 命名规范
  2. 异步方法名以Async后缀标识,如sendEmailAsync
  3. 线程池按业务命名,如orderTaskExecutor
  4. 日志记录
  5. 记录异步任务开始/结束时间
  6. 为异步线程设置可追踪的上下文ID
@Async
public void asyncWithLogging() {
    String traceId = UUID.randomUUID().toString();
    MDC.put("traceId", traceId);
    try {
        log.info("异步任务开始");
        // 业务逻辑
        log.info("异步任务完成");
    } finally {
        MDC.clear();
    }
}
  1. 防御性编程
  2. 检查异步方法参数有效性
  3. 添加合理的超时控制
  4. 资源限制
  5. 限制并发异步任务数量
  6. 对重要任务设置优先级
  7. 监控告警
  8. 监控线程池关键指标
  9. 设置异常告警阈值

七、与其他技术的整合

7.1 与Spring Retry整合

实现异步任务失败重试:

@Async
@Retryable(value = {RemoteAccessException.class}, 
           maxAttempts = 3, 
           backoff = @Backoff(delay = 1000, multiplier = 2))
public CompletableFuture<String> callExternalService() {
    // 调用可能失败的外部服务
    return CompletableFuture.completedFuture(externalService.call());
}

// 重试全部失败后的处理
@Recover
public CompletableFuture<String> recover(RemoteAccessException e) {
    return CompletableFuture.completedFuture("默认返回值");
}

7.2 与Spring Cache整合

异步缓存更新:

@Async
@CacheEvict(value = "users", key = "#userId")
public void evictUserCacheAsync(String userId) {
    // 异步清理缓存
}

@Async
@CachePut(value = "users", key = "#user.id")
public CompletableFuture<User> updateUserAsync(User user) {
    // 异步更新用户并更新缓存
    return CompletableFuture.completedFuture(userRepository.save(user));
}

7.3 与WebFlux响应式编程对比

特性

@Async

WebFlux

编程模型

命令式

响应式

线程模型

线程池-based

事件循环

资源消耗

较高(每个请求一个线程)

较低(少量线程处理所有请求)

学习曲线

较低

较高

适用场景

传统Servlet应用

高并发IO密集型应用

背压支持

不支持

支持

集成复杂度

简单

中等

八、常见问题与解决方案

8.1 问题排查表

问题现象

可能原因

解决方案

@Async方法不异步执行

同类调用

确保通过Spring代理调用,使用@Autowired注入自身


未加@EnableAsync

在主配置类添加@EnableAsync

异步方法抛出异常不显示

未正确处理AsyncUncaughtException

实现
AsyncUncaughtExceptionHandler

线程池不生效

未正确命名或注入

确保@Async("executorName")与@Bean名称一致

应用关闭时任务丢失

未配置优雅关闭

设置
setWaitForTasksToCompleteOnShutdown(true)和awaitTerminationSeconds

性能未提升反而下降

线程池配置不合理

调整核心/最大线程数和队列容量

ThreadLocal值丢失

线程切换导致上下文丢失

使用TaskDecorator传递上下文

8.2 实战问题案例

案例1:数据库连接泄漏

@Async
public void processData() {
    // 错误示范:未关闭Connection
    Connection conn = dataSource.getConnection();
    // 使用conn...
}

解决方案

@Async
public void processData() {
    try (Connection conn = dataSource.getConnection()) {
        // 使用conn...
    } catch (SQLException e) {
        // 异常处理
    }
}

案例2:订单超时未支付取消

@Async
@Scheduled(fixedDelay = 60000)  // 每分钟检查一次
public void cancelUnpaidOrders() {
    List<Order> unpaidOrders = orderRepository.findByStatusAndCreateTimeBefore(
        OrderStatus.UNPAID, 
        LocalDateTime.now().minusMinutes(30));
    
    unpaidOrders.forEach(order -> {
        order.setStatus(OrderStatus.CANCELLED);
        orderRepository.save(order);
        notificationService.sendCancelNotice(order);
    });
}

九、总结

9.1 核心要点总结

  1. 基础使用
  2. @EnableAsync启用支持
  3. @Async标注异步方法
  4. 避免同类调用
  5. 线程池配置
  6. 生产环境必须自定义线程池
  7. 合理设置核心参数
  8. 监控线程池状态
  9. 异常处理
  10. 区分返回值类型选择处理方式
  11. 实现全局异常处理器
  12. 日志记录完整上下文
  13. 高级特性
  14. 组合多个异步操作
  15. 事务边界处理
  16. 条件异步执行
  17. 最佳实践
  18. 命名规范
  19. 防御性编程
  20. 资源清理
  21. 上下文传递

9.2 完整示例代码

订单处理服务示例

@Service
public class OrderProcessingService {
    
    private static final Logger logger = LoggerFactory.getLogger(OrderProcessingService.class);
    
    @Autowired
    private OrderRepository orderRepository;
    
    @Autowired
    private PaymentService paymentService;
    
    @Autowired
    private NotificationService notificationService;
    
    @Async("orderTaskExecutor")
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public CompletableFuture<OrderResult> processOrderAsync(Order order) {
        logger.info("开始异步处理订单: {}", order.getId());
        long startTime = System.currentTimeMillis();
        
        try {
            // 1. 保存订单
            Order savedOrder = orderRepository.save(order);
            
            // 2. 处理支付
            PaymentResult paymentResult = paymentService.processPayment(savedOrder);
            if (!paymentResult.isSuccess()) {
                throw new PaymentException("支付处理失败: " + paymentResult.getErrorMessage());
            }
            
            // 3. 更新订单状态
            savedOrder.setStatus(OrderStatus.PAID);
            orderRepository.save(savedOrder);
            
            // 4. 发送通知
            notificationService.sendOrderConfirmation(savedOrder);
            
            long elapsed = System.currentTimeMillis() - startTime;
            logger.info("订单处理完成: {}, 耗时: {}ms", savedOrder.getId(), elapsed);
            
            return CompletableFuture.completedFuture(
                new OrderResult(true, "订单处理成功", savedOrder));
                
        } catch (PaymentException e) {
            logger.error("订单支付异常: {}", order.getId(), e);
            return CompletableFuture.completedFuture(
                new OrderResult(false, e.getMessage(), order));
        } catch (Exception e) {
            logger.error("订单处理未知异常: {}", order.getId(), e);
            return CompletableFuture.failedFuture(e);
        }
    }
    
    // 批量异步处理订单
    @Async("batchOrderExecutor")
    public CompletableFuture<Void> processOrdersBatch(List<Order> orders) {
        List<CompletableFuture<OrderResult>> futures = orders.stream()
            .map(this::processOrderAsync)
            .collect(Collectors.toList());
            
        return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
            .exceptionally(ex -> {
                logger.error("批量处理订单异常", ex);
                return null;
            });
    }
}

// 配置类
@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
    
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(20);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("Order-Async-");
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setAwaitTerminationSeconds(60);
        executor.initialize();
        return executor;
    }
    
    @Bean(name = "batchOrderExecutor")
    public Executor batchOrderExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(50);
        executor.setThreadNamePrefix("Batch-Order-");
        executor.initialize();
        return executor;
    }
    
    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new OrderAsyncExceptionHandler();
    }
}

// 全局异常处理器
public class OrderAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
    private static final Logger logger = LoggerFactory.getLogger(OrderAsyncExceptionHandler.class);
    
    @Override
    public void handleUncaughtException(Throwable ex, Method method, Object... params) {
        logger.error("异步订单处理异常 - 方法: {}, 参数: {}", method.getName(), Arrays.toString(params), ex);
        
        // 发送告警邮件
        if (ex instanceof CriticalOrderException) {
            sendCriticalAlert(method, ex, params);
        }
    }
    
    private void sendCriticalAlert(Method method, Throwable ex, Object... params) {
        // 实现告警逻辑
    }
}

头条对markdown的文章显示不太友好,想了解更多的可以关注微信公众号:“Eric的技术杂货库”,后期会有更多的干货以及资料下载。



相关推荐

R数据分析:双因素方差分析与交互作用检验

Two-wayANOVAtestisusedtoevaluatesimultaneouslytheeffectoftwogroupingvariables(AandB)...

R数据分析:用R语言做meta分析

这里以我的一篇meta分析为例,详细描述meta分析的一般步骤,该例子实现的是效应量β的合并...

生存分析看完这篇就够了(R语言代码+线上工具)

尔云间一个专门做科研的团队云生信学生物信息学关注我们...

用R语言画图

R语言中离群值的识别、描述、绘制与移除

摘要:统计学中离群值被定义为离开大部分观测较远的样本点,多数是由于测量误差而产生。因此,数据分析中离群值的识别和移除(如有必要)是很重要的一个步骤。鉴定离群值的方法有很多种,包括基于标准差的方法和基于...

R语言、RStudio的下载、安装与使用

本文介绍R语言及其集成开发环境...

[R语言] R语言快速入门教程

本文主要是为了从零开始学习和理解R语言,简要介绍了该语言的最重要部分,以快速入门。主要参考文章:R-TutorialR语言程序的编写需要安装R或RStudio,通常是在RStudio中键入代码。但是R...

看完这个「R语言课程」合集,我发现R语言也不过如此…

小料君看到一个有趣的问答...

在R语言中使用正则表达式

有时候我们要处理的是非结构化的数据,例如网页或是电邮资料,那么就需要用R来抓取所需的字符串,整理为进一步处理的数据形式。R语言中有一整套可以用来处理字符的函数,在之前的博文中已经有所涉及。但真正的...

附代码|详解R语言的高级数据结构

有时数据需要比向量更复杂的存储方式。幸运的是,R软件提供了很多的数据结构。常见的有数据框(data.frame)、矩阵(matrix)、列表(list)以及数组(array)。数据框类似于电子表格,矩...

R语言实战—自学笔记—向量

第2章创建数据集2.1数据集的概念数据集:由数据构成的一个矩形数组,行表示观测,列表示变量。...

R语言从入门到精通:Day7

原创小浣熊科研猫是时候关注我们一波了在向reviewer或者导师展示自己的统计分析结果时,一张图往往顶得上千言万语;在刚接触到数据时,图也能帮助我们发现数据中潜在的模式或者其中的异常值,这两个例子...

一文教你学会维恩图的绘制——R语言

背景介绍维恩图用于展示在不同的事物群组(集合)之间的数学或逻辑联系,尤其适合用来表示集合(或)类之间的“大致关系”,它也常常被用来帮助推导(或理解推导过程)关于集合运算(或类运算)的一些规律。通常每个...

R语言介绍

一、R语言介绍...

R语言的一些常见初级基本操作

基本概念:在R语言里操作和接触的所有东西都称作对象(object)。对象有很多种类可以包含各种类型的数据。R语言里所有的东西都被称为对象,R语言中常见的数据类型有几下几种,分别是字符型(cha...