关于线程安全的涉及到概念以及分类

来源:互联网 发布:python需要设计模式吗 编辑:程序博客网 时间:2024/06/05 20:22

本文摘选自深入理解Java虚拟机。

方法一:互斥同步(Matual Exclusion & Synchronization)

互斥同步属于一种悲观的并发策略,总是认为只要不去做正确的同步措施,就肯定会出现问题。无论共享数据是否出现竞争,它都要进行加锁。故称互斥同步的锁为悲观锁

①使用synchronized关键字。synchronized同步代码块对同一个线程来说是可重入的,不会出现自己吧把自己锁死的问题。


②使用ReentrantLock重入锁来实现同步。ReentrantLock与synchronized很相似。都是对同一个线程可重入。不过ReentrantLock增加了一些高级的功能:等待可中断,可实现公平锁。锁可以绑定多个条件。


等待可中断是指:当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待。改为处理其他事情,可中断特性对处理执行时间非常长的同步块很有帮助。


公平锁:多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁。而非公平锁不保证这一点:在锁被释放的时候,任何一个等待锁的线程都有机会获得锁(通过竞争获得锁)。synchronized中的锁是非公平锁,ReentrantLock默认情况下也是非公平锁,但是可以通过带布尔值的构造函数来构造公平锁。


锁绑定多个条件是指一个ReentrantLock对象可以同时绑定多个Condition对象。在synchronized中,隐含着绑定一个对象,如果要是有多个对象关联的时候,就要多加一个额外的锁。ReentrantLock则不需要这样做,只需要多次调用newCondition()方法即可。


方法二:非阻塞同步(又称乐观锁)


互斥同步最主要的问题就是进行线程阻塞和唤醒所带来的性能问题,因此这种同步也称为阻塞同步(Blocking Synchronization)。所以就有了一种不需把线程挂起的同步操作称之为非阻塞同步(Non-Blocking Synchronization):一种基于冲突检测的乐观并发策略,通俗了说,就是先进行操作,如果没有其他线程争用共享数据,那操作就成功了;如果共享数据有争用,产生了冲突,那就采用其他的补偿措施(最常见的的就是不断重试,直到成功为止)。在Java中常见的就是Compare-and Swap(CAS)指令。CAS指令有三个操作数,分别是内存位置的值(用V表示),旧的预期值A,新值B,CAS指令执行的时候,当且仅当V符合旧预期值A时,处理器用新值B更新V的值,否则 就不执行更新,无论是否更新V的值,都会返回V的旧值。

        CAS指令这个执行过程是个原子操作。其他的原子操作还有指令:测试并设置(Test- And-Set)、获取并增加(Fetch-And-Increment)、交换(Swap)、加载链接/条件存储(Load-Linked/Store-Conditional)。

方法三:无同步方案


如果一个方法本来就不涉及共享数据,那它自然就不需要任何同步措施去保证正确性,因此会有一些代码天生就是线程安全的。

一类:可重入代码(Reentrant Code):这种代码叫做纯代码,可以在代码执行的任何时刻去中断它,转而去执行另一段代码,而在控制权返回后,原来的程序不会出现任何错误。判断是否可重入性的原则:如果一个方法,他的返回结果是可以预测的,只要输入了相同的数据,就能返回相同的结果,那它就满足可重入性的要求。


二类:线程本地存储(Thread Local Storage):如果一段代码中所需要的数据必须与其他代码共享数据。如果能保证这些共享数据的代码在同一个线程中执行,那么这些共享数据的可见范围在同一个线程之内。这样不需要同步也能保证线程之间不出现数据争用的问题。在Java中,通过java.lang.ThreadLocal 类来实现线程本地存储的功能。

原创粉丝点击