`
xussen
  • 浏览: 29954 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

线程安全与锁优化

 
阅读更多
Brian Goetz对线程安全的定义:当多个线程访问一个对象时,如果不考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调度方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象就是线程安全的

并发处理的广泛应用是使得Amdahl定律替代摩尔定律成为计算机性能发展源动力,是人类压榨计算机运算能力最有力的武器

线程安全 限定为多个线程之间存在共享数据访问。因为如果多个线程之间不存在共享数据的话,那么从线程安全的角度看,程序串行和多线程执行时完全没有区别的。

java里面安全程度由强到弱排序(也是由Brian Goetz提出的):不可变,绝对线程安全,相对线程安全,线程兼容,线程对立

不可变:可以是基本类型的final;可以是final对象,但对象的行为不会对其状态产生任何影响,比如String的subString就是new一个String对象
各种Number类型如BigInteger和BigDecimal等大数据类型都是不可变的,但是同为Number子类型的AtomicInteger和AtomicLong则并非不可变
我觉得原因是它里面状态对象时unsafe对象,所做的操作都是CAS操作,可以保证原子性。

绝对线程安全:他是完全满足Brian Goetz给出的线程安全的定义,一个类要达到这种程度,需要付出很大的,甚至不切实际的代价。
java里面很多看上去非常安全的类,比如vector其实也不能满足这一点。例子:
线程A
for(int i=0; i<vectoe.size();i++) {
vector.remove(i);
}
线程B
for(int i=0; i<vectoe.size();i++) {
vector.get(i);
}
会报ArrayIndexOutOfBoundsException。
需要额外的同步
线程A
synchronized{
for(int i=0; i<vectoe.size();i++) {
vector.remove(i);
}
}
线程B
synchronized{
for(int i=0; i<vectoe.size();i++) {
vector.get(i);
}
}
相对线程安全:这就是我们通常意义上的线程安全。需要保证对象单独的操作时线程安全的。
比如vector,hashtable,synchronizedCollection包装集合
线程兼容:对象本身不是线程安全的,但可以通过同步手段实现。一般我们说的不是线程安全的,绝大多数是指这个。
比如ArrayList,HashMap等
线程对立:不管调用端是否采用了同步的措施,都无法在并发中使用的代码。
调用suspend()的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。此时,其他任何线程都不能访问锁定的资源,除非被"挂起"的线程恢复运行。对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。

线程安全的实现
1、互斥同步
在多线程访问的时候,保证同一时间只有一条线程使用。
临界区(Critical Section),互斥量(Mutex),信号量(Semaphore)都是同步的一种手段
java里最基本的互斥同步手段是synchronized,编译之后会形成monitorenter和monitorexit这两个字节码指令,这两个字节码都需要一个reference类型的参数来指明要锁定和解锁的对象(可以通过工具读class文件,这是以后必须要做的),还有个锁的计数器,来记录拥有锁的次数,跟AQS里面的state一样
其实在“Java与线程”里已经提到,java的线程是映射到操作系统的原生线程之上的,不管阻塞还是唤醒都需要操作系统的帮忙完成,都需要从用户态转换到核心态,这是很耗费时间的,是java语言中的一个重量级(Heavyweight)操作,虽然虚拟机本身会做一点优化的操作,比如通知操作系统阻塞之前会加一段自旋等待的过程,避免频繁切换到核心态。
ReentrantLock也是一个很好的选择。
1)ReentrantLock比synchronized增加了一些高级的功能
2)从性能角度考虑,在JDk1.5时代,多线程环境下synchronized的吞吐量下降得非常严重,而ReentrantLock则能保持在比较稳定的水平线上,但是从1.6开始两者性能上基本持平。所以现在这个理由已经不再是了。
虚拟机未来一定是向原生的synchronized改进,所以提倡在synchronized能实现需求的情况下,优先考虑synchronized
2、非阻塞同步
互斥和同步最主要的问题就是阻塞和唤醒所带来的性能问题,所以这通常叫阻塞同步(悲观的并发策略)
随着硬件指令集的发展,我们有另外的选择:基于冲突检测的乐观并发策略,通俗讲就是先操作,如果没有其他线程争用共享的数据,操作就成功,如果有,则进行其他的补偿(最常见就是不断的重试),这种乐观的并发策略许多实现都不需要把线程挂起
我的理解就是把那些 需要同步需要多个操作完成的任务沉到硬件指令来完成
这类的指令有:
1)测试并设置(test-and-set)
2)获取并增加
3)交换
4)比较并交换(CAS)
5)加载链接/条件储存(Load-Linked/Store-Conditional  LL/SC)
后面两条是现代处理器新增的处理器指令,在JDK1.5之后,java中才可以使用CAS操作,就是传说中的sun.misc.Unsafe类里面的compareAndSwapInt()和compareAndSwapLong()等几个方法的包装提供,虚拟机对这些方法做了特殊的处理,及时编译出来的结果就是一条平台相关的处理器CAS指令,没有方法调用的过程,可以认为是无条件的内联进去。
原来需要对i++进行同步,但现在有了这种CAS操作来保证原子性,比如用AtomicInteger。
但是不要忘记了CAS存在一个ABA的问题。可以通过AtomicStampedReference来解决
3、无同步方案
要保证线程安全,并不一定就要进行同步,没因果关系。
锁优化
果高效并发是JDK1.6的重要主题(所以我们都会觉得直接跳过1.5用1.6),HotSpot虚拟机开发团队花大量精力实现锁的优化技术
如:适应性自旋、锁消除、锁粗化、轻量级锁、偏向锁等
什么是自旋锁:互斥同步最大的问题就是阻塞的实现会影响性能,挂起和恢复线程的操作都需要转入内核态中完成。同时虚拟机的开发团队注意到许多应用上,共享数据的锁状态只会持续很短的时间,为了这段时间去挂起和恢复线程不值得。可以让请求锁的线程稍等一会儿,看看持有锁的线程是否很快释放所。为了让线程等待,我们只须让线程执行一个忙循环(自旋),这项技术就是所谓的自旋锁
什么是自适应自旋:1.6引入了自适应自旋锁,自旋锁的时间不再是固定的,而是由前一次的在同一个锁上的自旋时间及锁的拥有者的状态决定。就是虚拟机对程序锁的状态的预测变得越来越聪明
锁消除:指虚拟机即时编译器在运行时,对一些代码上要求同步,但被检测到不可能存在共享数据竞争的锁进行消除。判断依据来源于逃逸分析的数据支持。如Stringbuffer的例子,所有方法都是synchronized,但是虚拟机观察对象永远不会“逃逸”到客户端方法外,在及时编译之后,所有同步会被忽略掉。
锁粗化:比如Stringbuffer的append("a").append("b").append("c")会将synchronized的范围扩大到整个
轻量级锁:JDK1.6加入的新型锁机制。具体原理还是无法理解,讲的比较忽悠人
偏向锁:JDK1.6引入的一项锁优化。如果说轻量级锁是在无竞争的情况下使用CAS操作去消除同步使用的互斥量,那偏向锁就是在无竞争的情况下把整个同步都消除掉,连CAS操作都不做了。JDK6中,偏向锁默认打开,它提高了单线程访问同步资源的性能,但如果你的资源或代码移植处在多线程的环境下,并且对自己的代码非常熟悉,大可禁用偏向锁。-XX:-UseBiasedLocking
分享到:
评论

相关推荐

    【Java正来-深入理解JVM】线程安全与优化。xmind思维导图

    线程安全与锁优化:当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者再调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果。

    srzyhead#ReadingNote#13-线程安全与锁优化1

    绝对线程安全Vector的get()、remove()和size(),如果另一个线程恰好在错误的时间里删除了一个元素,导致序号i已经不再可用的话,再用i访问数组

    java虚拟机知识点整理

    线程安全与锁优化 1 标记-清除算法:首先标记所有需要回收的对象(引用计数或可达性分析算法标记),在标记完成后统一回收所有被标记的对象。 缺点:效率问题,标记和清除两个过程效率都不高,另一个是清除之后会产生...

    深入理解_Java_虚拟机 JVM_高级特性与最佳实践

    / 342 13.1 概述 / 342 13.2 线程安全 / 343 13.2.1 Java语言中的线程安全 / 343 13.2.2 线程安全的实现方法 / 348 13.3 锁优化 / 356 13.3.1 自旋锁与自适应自旋 / 356 13.3.2 锁消除 / 357 13.3.3 锁粗化 ...

    Java虚拟机

    第13章 线程安全与锁优化 13.1 概述 13.2 线程安全 13.2.1 Java语言中的线程安全 13.2.2 线程安全的实现方法 13.3 锁优化 13.3.1 自旋锁与自适应自旋 13.3.2 锁消除 13.3.3 锁粗化 13.3.4 轻量级锁 13.3.5...

    java面试第二部分:多线程与锁

    锁升级、虚拟机锁优化、锁的实现原理、线程安全、可重入锁、线程池的实现等常见的面试问题

    优化版的环形缓存器 线程安全 生产者消费者模式

    之前上传过一个环形缓存器(这是一个基本的生产者消费者模式的环形缓存器,具有线程安全性,可同时写入和读取,非装箱拆箱操作,高性能大数据可使用此缓存器。),这个是优化版,增加了缓存器满的标志位,优化了内存...

    Java多线程和并发知识整理

    1.5线程安全的分类 1.6线程安全的方法 二、线程基础 2.1状态 2.2使用方式 2.3基础机制 2.4中断 2.5互斥同步 2.6线程合作 三、Synchronized 详解 3.1 使用 3.2 原理分析 3.3 JVM中锁的优化 3.4 ...

    Java 多线程编程面试集锦20道问题解答Java多线程编程高难度面试题及解析

    您将了解线程安全的实现、死锁的避免策略、线程池的使用方法、线程上下文切换的原因与优化、线程同步与互斥的区别、volatile关键字的作用、synchronized关键字的用法等。同时,我们还探讨了多线程编程中 通过研究和...

    hashmap:一种Golang无锁,线程安全的HashMap,针对最快的读取访问进行了优化

    哈希图总览Golang无锁无线程安全HashMap,针对最快的读取访问进行了优化。用法为地图中的键设置值: m := &HashMap{}m.Set("amount", 123)从地图中读取键的值: amount, ok := m.Get("amount")使用地图来计数URL请求...

    深入理解线程安全与Singleton

    线程安全是个非常棘手的问题。即使你合理的使用了锁(lock),依然可能不会产生预期的效果。让我们来看看貌似合理的代码 代码如下:X=0;Thread 1 Thread2lock(); lock();x++; x++;unlock(); unlock();你会认为...

    理解原子操作,CAS加锁是线程安全的.docx

    在Java中实现并发用的最多的就是synchronized关键字了,自从jdk1.6对synchronized进行重大优化后,其广为人诟病的性能问题也得到了改善,与ReentrankLock相比性能方面相差无几 性能的改善得益于 偏向锁、轻量级锁 ...

    服务器监控及性能优化.pptx

    MMO服务器常用的优化手段 线程操作优化 ----尽量减少锁的时间 尽可能的少调用锁 减小锁粒度 线程数控制,线程间切换开销 利用析构自解锁,防止死锁 网络 数据库 存读档 内部LOG 系统 游戏场景 。。。 一次交换收到...

    Java面试题-并发.docx

    特别强调了Hashtable不允许插入null的原因,以及ConcurrentHashMap在线程安全实现和锁优化方面的策略。 总的来说,这份文档对HashMap的各个方面进行了全面而详细的阐述,既适合作为面试准备的参考资料,也适用于...

    Java面试题-哈希.docx

    特别强调了Hashtable不允许插入null的原因,以及ConcurrentHashMap在线程安全实现和锁优化方面的策略。 总的来说,这份文件对HashMap的各个方面进行了全面而详细的阐述,既适合作为面试准备的参考资料,也适用于...

    构建最高可用Oracle数据库系统 Oracle 11gR2 RAC管理、维护与性能优化

    13.1.3日志线程与联机Redo日志 13.1.4 UNDO表空间 13.2实例恢复 13.2.1 RAC的实例恢复 13.2.2实例恢复的阶段 13.3介质恢复 13.3.1介质恢复的过程 13.3.2物理坏块和逻辑坏块 13.3.3坏块的检测工具 13.3.4块...

    阿里百度美团面试题合集

    数据结构与算法:最常见的各种排序,最好能手写 .. Java 高级:JVM 内存结构、垃圾回收器、回收算法、GC、并发编程相关(多 线程、线程池等)、NIO/BIO、各种集合类的比较优劣势... 乐观锁如何保证线程安全? . 用过线

    C#开发常见问题清单总结与入门常见问题.docx

    技巧12:线程安全编程与锁机制的正确使用 5. .NET框架高级特性 技巧13:LINQ查询表达式的高效运用 技巧14:Lambda表达式与委托的简化编程 技巧15:异步编程与Task Parallel Library(TPL)的集成 6. 应用场景实例 ...

    java高并发相关知识点.docx

    线程安全:Java中的线程安全,包括同步方法和同步块等。 死锁:Java中的死锁,包括如何避免死锁和如何解除死锁。 性能优化:Java中的性能优化,包括JVM参数调优、代码优化、使用并发框架等。 并行计算:Java中的并行...

    Java并发编程(学习笔记).xmind

    事件处理器与访问共享状态的其他代码都要采取线程安全的方式实现 框架通过在框架线程中调用应用程序代码将并发性引入应用程序,因此对线程安全的需求在整个应用程序中都需要考虑 基础知识 线程安全性 ...

Global site tag (gtag.js) - Google Analytics