参考《深入了解Java虚拟机,JVM高级特性与最佳实践》
线程安全的实现方法
互斥同步:
指再多线程访问贡献数据是,保证该数据在同一时刻只能被一个(或一些)线程使用。临界区(Critical Section)、互斥量(Mutex)和信号量(Semaphore)都是互斥的主要手段
1.synchronized :
synchronized经过编译之后会在同步代码块前后形成monitorenter和monitorexit字节码指令,这两个字节码都需要一个reference类型的参数来指明要锁定和解锁的的对象。如果Java程序中的synchronized关键字明确制定了对象参数,那就是这个对象的reference,如果没有指定,则根据修饰是实例方法还是类方法,对应对象实例和类的Class对象.
在执行monitorenter指令时,首先要尝试获取对象的锁,如果这个对象没有被锁定,或者当前线程已经拥有了那个对象的锁,把锁的计数器加1,相应的 ,在执行monitorexit指令时,会将锁计数器减1,当计数器为0时,锁就被释放.如果获取对象失败,那当前线程就要阻塞等待,知道励对象锁被另一个线程鞥释放为止.
2.java.util.concurrent.ReentrantLock:
在基本用法上,ReentrantLock与synchronized很相似 ,他们都具备一样的线程冲入特性,在代码写法上有点区别,一个表示为API层面的互斥锁(lock()和unlock()方法配合try/finally来完成),另一个表现为原生语法层面的互斥锁.ReentrantLock可能现在以下高级功能:等待可中断、可实现公平锁,以及可以绑定多个条件。
等待可中断:当尺有所的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情,可中断特性对处理执行时间非常长的同步块很有帮助。
公平锁:多个线程在等待同一个锁时,必须按照申请所的时间顺序来一次获取锁,而非公平锁则不保证这一点,在锁被释放时,任何一个等待锁的线程都有机会获取锁。synchronized中的锁就是非公平的,ReentrantLock默认情况下也是非公平的,但可以通过带布尔值的构造函数要求使用公平锁.
锁绑定多个条件:指一个ReentrantLock对象可以同时绑定多个Condition对象,而在synchronized中,锁对象的wait()和notify()或者notifyAll()方法可以实现一个隐含的条件,如果要和多于一个的条件关联的时候,就不得不额外的添加一个锁,但而RenntrantLock则无需这样做,只需要多次调用newCondition()方法即可.
JDK1.6之前, 多线程环境下synchronized的吞吐量下降的非常严重,而ReentrantLock则能基本保持在同一个比较稳定的水平上。JDK1.6之后synchronized关键字得到优化,性能基本与ReentrantLock无异,由于在未来的性能改进中肯定也会更加偏向于原生的synchronized,在能实现需求的情况下,优先考虑synchronized来使用同步
互斥同步最主要的问题就是进行线程阻塞和唤醒所带来的的性能问题
非阻塞同步:
基于冲突检测的乐观并发策略,通俗的说,就是先进性操作,如果没有其他线程争用共享数据,那操作就成功了;如果共享数据有争用,产生了冲突,再采取其他补偿措施(最常见的补偿措施就是不断的重试,直到成功为止),这种乐观的并发策略的许多实现都不需要把线程挂起,因此这种同步操作称为非阻塞同步(Non-Blocking Synchronization)
无同步方案:
同步只是保证共享数据争用时正确的手段,如果一个方法本来就不涉及共享数据,那它就无需任何同步去保证正确性,例如:
可重入代码:这种代码也叫做纯代码,可以在代码执行的任何时刻中断它,转而去执行另外一段代码,而在控制权返回后,原来的程序不会出现任何错误.
线程本地存储:如果一段代码中所需要的数据必须与其他代码共享,那就看看共享数据的代码能否保证在同一个线程中进行,如果可以,则无需同步,也可以使用线程本地存储来解决安全问题.
网友评论