多线程使用时,有一个常见的问题是变量共享问题。在多线程环境中,多个线程可以同时访问和修改同一个变量,这时就会出现竞态条件和数据不一致的问题。
例如,假设有两个线程同时对一个变量进行递增操作,代码如下:
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
变量用于记录计数值。我们启动了两个线程 thread1
和 thread2
,它们分别对计数器进行了一万次递增操作,并打印最终的计数值。
然而,由于两个线程同时对 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);
}
}
}