什么是线程安全,如何实现

定义:当多个线程访问同一对象时,不需要考虑这些线程在运行环境下的调度和交替执行,不需要进行额外的同步操作,不需要再调用方进行额外的协调操作,调用这个方法都能获得正确的结果,那这个对象就是线程安全的。就是说,代码本身封装了所有保障正确性的手段,调用方无需关心多线问题,更无需采取任何措施来保障多线程的正确调用。

举几个例子,可以将JAVA语言中操作共享的数据分为5类,不可变,绝对线程安全,相对线程安全,线程兼容,和线程对立。

不可变的对象一定是线程安全的,不管是对象的方法实现还是方法调用者,都不需要采取任何措施去保障线程安全。不可变带来的线程安全时最简单,最存粹的,如果对象是基本类型,则通过final修饰符即可实现不可变,如果对象时引用类型,则需要保证对象的行为不会使对象的状态发生变化才行。

绝对线程安全的定义十分严格,“不管在任何情况下,调用者都不需要采取同步措施”,例如JAVA  API提供的线程安全的类,在复杂的运行环境下,仍需要额外的同步操作来保障线程安全。

相对线程安全,就是我们通常意义上的线程安全,它只需要保证对这个对象的单独操作是线程安全的,不需要额外的操作来保证线程安全,但是对于特定顺序的连续调用,仍需要额外同步操作来保障线程安全。

线程兼容是指对象本身是线程不安全的,在并发环境下必须通过额外的线程同步操作来保障线程安全。我们通常意义上理解的不安全的类,就是这种情况。

线程对立是指无论采取什么措施都无法保障对象在多线程环境下的线程安全,这种情况在JAVA中很少出现,而且通常是有害的,应避免出现,例如被废弃的Thread类 的Suspend()和resume()方法。

实现方法

互斥同步:同步是指在多个线程并发访问共享数据时,只允许同一时刻只被一个线程使用,主要实现互斥的手段有,临界区,互斥量,心信号量。互斥是方法,同步是结果。

最基本的互斥手段就是synchronized关键字,synchronized同步块对于同一个线程来说是可重入的,不会出现把直接锁死的问题,线程进入同步块之后,会阻止其他线程进入同步块。

除了synchronized,还有就是reentrantLock可重入锁,相比synchronized,ReentrantLock增加了一些高级功能,主要有以下3项:等待可中断、可实现公平锁,以及锁可以绑定多个条件。

  1. 等待可中断是指当持有所得线程长时间不释放锁的时候,等待的线程可以放弃等待,改为处理其他事情,可中断特性对处理时间特别长的同步块很有帮助。
  2. 公平锁是指多个线程等待同一个共享数据时,必须按照申请锁的时间顺序还获得锁,非公平锁不能保证这一点,在锁释放时,任何一个等待线程都有机会获得锁,synchronized就是非公平锁,ReentrantLock默认也是非公平的,可以通过设置实现公平锁。
  3. 锁可以绑定多个条件是指一个RenentrantLock对象可以同时绑定多个condition对象,而在synchronized中,锁对象的wait()和notify()或notifyAll()方法可以实现一个隐含的条件,如果要和多于一个的条件关联的时候,就不得不额外地添加一个锁,而ReentrantLock则无须这样做,只需要多次调用newCondition()方法即可。

非阻塞同步:互斥同步也称为阻塞同步,在进行线程阻塞和线程唤醒时会存在性能问题。随着硬件指令集的发展,可以实现基于冲突检测的乐观并发策略,就是先执行操作,如果没法说资源竞争,那操作就成功了,如果发生了,在想办法补救。这种乐观的并发策略的许多实现都不需要将线程挂起,因此称为非阻塞同步。

如果一个方法本来就不涉及数据共享,那这些代码自然就是线程的安全的,比如可重入代码,线程本地存储

可重入代码也叫纯代码,可以在代码执行的任何时间中断它转而去执行其他代码,而在控制权返回后,不会发生任何错误,可重入的代码必然都是线程安全的。可重入代码有一些共性,比如不依赖堆上的数据和公共的系统资源,所需要的状态量都是由参数传入的,也不调用非可重入的代码。

线程本地存储,如果一段代码中所需要的数据必须与其他代码共享,那就看看这些共享数据的代码是否能保证在同一个线程中执行?如果能保证,我们就可以把共享数据的可见范围限制在同一个线程之内,这样,无须同步也能保证线程之间不出现数据争用的问题。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
线程安全是指在多线程环境下,对共享资源的访问和操作能够保证正确性和一致性,不会出现数据竞争和不确定的结果。 实现线程安全的方法有多种,以下是几种常见的方式: 1. 互斥锁(Mutex):使用互斥锁来保护临界区,确保同一时间只有一个线程可以访问共享资源。线程在访问共享资源前先获取锁,操作完成后释放锁。这样可以避免多个线程同时访问导致的数据竞争。 2. 读写锁(ReadWrite Lock):当共享资源多读少写时,使用读写锁可以提高并发性能。读写锁允许多个线程同时进行读操作,但只允许一个线程进行写操作。 3. 原子操作(Atomic Operation):原子操作是不可分割的操作,可以保证在多线程环境下的原子性。例如,使用原子整型变量来实现计数器,在对计数器进行自增或自减操作时,保证操作的完整性。 4. 使用线程安全的数据结构:一些数据结构本身就是线程安全的,如ConcurrentHashMap、ConcurrentLinkedQueue等。使用这些数据结构可以避免手动实现线程安全的逻辑。 5. 同步关键字(synchronized):在Java中,可以使用synchronized关键字来实现线程安全。通过对共享资源的访问添加同步块或同步方法,确保同一时间只有一个线程可以访问。 6. 使用线程安全的库或框架:一些编程语言提供了线程安全的库或框架,可以直接使用这些库或框架来简化线程安全实现。 无论使用哪种方式实现线程安全,都需要注意避免死锁和性能问题。在设计和实现时,需要综合考虑并发性能和数据一致性的需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值