多线程使用-变量共享问题

多线程使用时,有一个常见的问题是变量共享问题。在多线程环境中,多个线程可以同时访问和修改同一个变量,这时就会出现竞态条件和数据不一致的问题。

例如,假设有两个线程同时对一个变量进行递增操作,代码如下:

java 复制代码
public class Counter {
    private int count;

    public void increment() {
        count++;
    }

    public int getCount() {
        return count;
    }
}

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();

        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 100000; i++) {
                counter.increment();
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 100000; i++) {
                counter.increment();
            }
        });

        thread1.start();
        thread2.start();

        thread1.join();
        thread2.join();

        System.out.println(counter.getCount());
    }
}

在这个例子中,我们创建了一个计数器类 Counter,其中有一个 count 变量用于记录计数值。我们启动了两个线程 thread1thread2,它们分别对计数器进行了一万次递增操作,并打印最终的计数值。

然而,由于两个线程同时对 count 变量进行修改,会导致竞态条件的产生。即,一个线程读取到了旧的值,然后修改后写入,之后另一个线程读取并修改,覆盖了前一个线程的修改结果,最终导致计数值不准确。

为了解决这个问题,可以使用锁机制来保证同一时间只有一个线程可以访问和修改变量。可以使用 synchronized 关键字或 ReentrantLock 类来实现锁机制。

java 复制代码
public class Counter {
    private int count;
    private Lock lock = new ReentrantLock();

    public void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }

    public int getCount() {
        return count;
    }
}

在这个修改后的代码中,我们引入了一个 lock 对象,用于进行锁定和解锁操作。在 increment() 方法中,我们先获取锁,然后进行递增操作,最后释放锁。这样就能保证同一时间只有一个线程可以修改计数值。

在实际开发中,变量共享问题是一个需要特别注意的问题,如果处理不当可能会引发严重的并发安全问题。因此,在多线程程序中,必须谨慎地处理共享变量,并使用合适的同步机制来保证数据的一致性和正确性。

@Async 异步使用

在Spring Boot中,我们可以使用@Async注解来实现异步方法调用。下面是使用@Async注解的示例代码:

首先,我们需要在Spring Boot的启动类上添加@EnableAsync注解来开启异步支持:

java 复制代码
@SpringBootApplication
@EnableAsync
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

然后,在需要异步执行的方法上添加@Async注解:

java 复制代码
@Service
public class MyService {

    @Async
    public void asyncMethod() {
        // 异步执行的代码
    }
}

最后,在调用异步方法的地方,可以直接调用该方法,方法的执行会在一个新的线程中进行:

java 复制代码
@RestController
public class MyController {

    @Autowired
    private MyService myService;

    @GetMapping("/async")
    public String asyncMethod() {
        myService.asyncMethod();
        return "Async method is invoked.";
    }
}

通过以上配置,当我们访问/async接口时,会触发异步方法的执行,并且可以立即返回结果。

需要注意的是,@Async注解默认使用Spring的SimpleAsyncTaskExecutor来执行异步方法,也可以自定义线程池来处理异步任务,可以通过在启动类中配置TaskExecutor bean来实现:

java 复制代码
@EnableAsync
@Configuration
public class AsyncConfig {

    @Bean(name = "taskExecutor")
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(100);
        executor.setQueueCapacity(10);
        executor.initialize();
        return executor;
    }
}

在自定义的线程池中,可以根据实际需求配置线程池的大小、队列容量等参数。

这样,通过@Async注解,我们可以方便地实现异步方法调用,并提高系统的并发处理能力。

以上设置没有问题。实际的开发中,可能会有@Async方法带参数的情况:

java 复制代码
@Service
public class MyService {
    @GetMapping("/async")
    public String asyncMethod(People people) {
        return "Async method is invoked.";
    }
}

asyncMethod方法如果在for循环中调用,一定不能共用实体类.都在在异步线程中会出现变量共享的问题。

java 复制代码
@RestController
public class MyController {

    @Autowired
    private MyService myService;

    @GetMapping("/people")
    public void asyncMethod(List<Man> list) {
    	// People people = new People();
       for(Man man :list){
       		// people 对象如果使用同一个,就会出现问题,必须每次new 一个新对象
       		People people = new People();
       		people.setId(man.getId);
       		myService.asyncMethod(people);
       }
    }
}
相关推荐
二哈赛车手6 小时前
新人笔记---ApiFox的一些常见使用出错
java·笔记·spring
为何创造硅基生物7 小时前
C语言 结构体内存对齐规则(通俗易懂版)
c语言·开发语言
吃好睡好便好7 小时前
在Matlab中绘制横直方图
开发语言·学习·算法·matlab
栗子~~7 小时前
JAVA - 二层缓存设计(本地缓冲+redis缓冲+广播所有本地缓冲失效) demo
java·redis·缓存
星寂樱易李7 小时前
iperf3 + Python-- 网络带宽、网速、网络稳定性
开发语言·网络·python
YDS8297 小时前
DeepSeek RAG&MCP + Agent智能体项目 —— RAG知识库的搭建和接口实现
java·ai·springboot·agent·rag·deepseek
仰泳之鹅7 小时前
【C语言】自定义数据类型2——联合体与枚举
c语言·开发语言·算法
之歆8 小时前
DAY_12JavaScript DOM 完全指南(二):实战与性能篇
开发语言·前端·javascript·ecmascript
未若君雅裁9 小时前
MyBatis 一级缓存、二级缓存与清理机制
java·缓存·mybatis
cen__y9 小时前
Linux12(Git01)
linux·运维·服务器·c语言·开发语言·git