java 并发 竞态条件(1)

简介:

竞态条件,说得通俗一点,就是线程A 需要判断一个变量的状态,然后根据这个变量的状态来执行某个操作。

在执行这个操作之前,这个变量的状态可能会被其他线程修改。

看一个例子

Java代码   收藏代码
  1. import java.util.concurrent.atomic.AtomicLong;  
  2.   
  3.   
  4. public class LazyInitRace {  
  5.     private ExpensiveObject instance=null;  
  6.       
  7.     public ExpensiveObject getInstance(){  
  8.         if(instance==null){  
  9.             instance=new ExpensiveObject();  
  10.         }  
  11.         return instance;  
  12.     }  
  13. }  

 在LazyInitRace 中包含了一个竞态条件,它可能会破坏这个类的正确性。假定线程A和线程B 同时执行getInstance 方法。A 看到instance 为空,因此A创建一个新的ExpensiveObject实例。B 同样需要判断instance 是否为空。此时的instance是否为空,要取决于不可预测的时序,包括线程的调度方式,以及A 需要花多长时间来初始化ExpensiveObject并设置instance。如果当B检查时,instance为空,那么在两次调用getInstance 时可能会得到不同的对象。

 

线程A和B的执行时序可能是这样的:

 说明:线程B 在执行红色部分代码(判断instance是否为空)时,线程A 还没来得执行完绿色部分的代码。

 

也可能是这样的:

 

有什么解决方案呢?

方案一:内置锁

每个Java对象都可以用作一个实现同步的锁。

线程进入内置锁保护的代码块时就拥有锁,执行完这段代码块时就自动释放锁。同一时刻只能有一个线程拥有锁,其他的线程只能等待。也就是说,当线程A 进入内置锁保护的代码块时,其他线程是不能执行这段代码的,只能等待线程A执行完毕。

线程安全的代码如下:

Java代码   收藏代码
  1. public class LazyInitRace {  
  2.     private ExpensiveObject instance=null;  
  3.       
  4.     public synchronized ExpensiveObject getInstance(){  
  5.         if(instance==null){  
  6.             instance=new ExpensiveObject();  
  7.         }  
  8.         return instance;  
  9.     }  
  10. }  

 

相关文章
|
1月前
|
存储 安全 算法
解读 Java 并发队列 BlockingQueue
解读 Java 并发队列 BlockingQueue
19 0
|
2月前
|
监控 安全 算法
Java并发基础:LinkedTransferQueue全面解析!
LinkedTransferQueue类实现了高效的线程间数据传递,支持等待匹配的生产者-消费者模式,基于链表的无界设计使其在高并发场景下表现卓越,且无需担心队列溢出,丰富的方法和良好的可扩展性满足了各种复杂应用场景的需求。
Java并发基础:LinkedTransferQueue全面解析!
|
2月前
|
Java 程序员 API
Java并发基础:concurrent Flow API全面解析
java.util.concurrent.Flow定义了响应式编程的核心接口,促进了Java在异步数据处理和背压机制方面的标准化,这使得第三方库如Reactor和RxJava能够基于这些接口提供丰富的实现和功能,同时简化了响应式编程在Java中的使用,Flow API增强了Java在并发编程领域的灵活性,使得处理异步数据流变得更加自然和高效。
107 0
Java并发基础:concurrent Flow API全面解析
|
2月前
|
安全 Java API
Java并发基础:Exchanger全面解析!
Exchanger类的优点在于能够简洁高效地实现两个线程间的数据交换,通过Exchanger,开发者可以避免复杂的锁和同步机制,降低并发编程的难度,同时,它还提供了线程安全的数据交换保障,使得多线程协作更为可靠、易用性和高效性。
Java并发基础:Exchanger全面解析!
|
1月前
|
存储 缓存 算法
Java并发基础:原子类之AtomicMarkableReference全面解析
AtomicMarkableReference类能够确保引用和布尔标记的原子性更新,有效避免了多线程环境下的竞态条件,其提供的方法可以轻松地实现基于条件的原子性操作,提高了程序的并发安全性和可靠性。
Java并发基础:原子类之AtomicMarkableReference全面解析
|
1天前
|
存储 缓存 安全
Java并发基础之互斥同步、非阻塞同步、指令重排与volatile
在Java中,多线程编程常常涉及到共享数据的访问,这时候就需要考虑线程安全问题。Java提供了多种机制来实现线程安全,其中包括互斥同步(Mutex Synchronization)、非阻塞同步(Non-blocking Synchronization)、以及volatile关键字等。 互斥同步(Mutex Synchronization) 互斥同步是一种基本的同步手段,它要求在任何时刻,只有一个线程可以执行某个方法或某个代码块,其他线程必须等待。Java中的synchronized关键字就是实现互斥同步的常用手段。当一个线程进入一个synchronized方法或代码块时,它需要先获得锁,如果
17 0
|
23天前
|
算法 安全 Java
Java中的并发编程:理解并发性能优化
在当今软件开发领域,多核处理器的普及使得并发编程变得更加重要。本文将深入探讨Java中的并发编程,介绍并发性能优化的关键技术,帮助开发人员更好地利用多核处理器提升应用程序性能。
|
1月前
|
安全 Java API
Java并发 - J.U.C并发容器类 list、set、queue
Queue API 阻塞是通过 condition 来实现的,可参考 Java 并发 - Lock 接口 ArrayBlockingQueue 阻塞 LinkedBlockingQueue 阻塞 ArrayQueue 非阻塞 LinkedQueue 非阻塞
|
1月前
|
安全 Java 调度
[Java并发基础] 共享内存
[Java并发基础] 共享内存
27 0
|
2月前
|
存储 安全 Java
Java并发基础:BlockingQueue和BlockingDeque接口的区别?
BlockingQueue 和 BlockingDeque 它们都支持在并发编程中的线程安全操作,但是,这两个接口之间存在一些关键的区别,主要在于它们所支持的操作和数据结构的特性,
Java并发基础:BlockingQueue和BlockingDeque接口的区别?