Java|伙计,提高自己的并发技能,从锁优化开始!( 三 )
自旋锁锁膨胀后 , 为了避免线程真实地在操作系统层面挂起 , 虚拟机还会做最后的努力——自旋锁 。 当前线程暂时获取不到锁 , 但是如果简单粗暴地将这个线程挂起是一种得不偿失的操作 , 因此虚拟机会让当前线程做几个空循环 , 在经过若干次循环后 , 如果可以得到锁 , 那么就顺利进入临界区 。
重量级锁如果经过自旋还不能获得锁 , 才会真的将线程在操作系统层面挂起 , 升级为 重量级锁 **
锁消除Java虚拟机在 JIT 编译时 , 会通过对运行上下文进行扫描 , 去除不可能存在共享资源竞争的锁 , 通过锁消除 , 可以节省毫无意义的请求锁时间 。
上面一段代码中 , 因为 vector 这个变量是定义在createArrays()这个方法中 , 是一个局部变量 , 在线程栈中分配的 , 属于线程私有的数据 , 因此不存在资源竞争的情况 。 而Vector内部所有加锁同步都是没有必要的 , 如果虚拟机检测到这种情况 , 就会将这些无用的锁操作去除 。
锁消除涉及的一项关键技术为逃逸分析 , 所谓逃逸分析就是观察某一个变量是否会逃出某一个作用域 。 在上面例子中 , 变量vector 没有逃出createArrays()这个函数的方位 , 因此虚拟机才会就将这个变量的加锁操作去除 。 如果 createArrays()返回的不是 String数组 , 而是 vector 本身 , 那么就认为变量 vector 逃出了当前函数 , 会被其他线程所访问到 。 例如下面代码:
【Java|伙计,提高自己的并发技能,从锁优化开始!】除了控制资源的访问外 , 我们还可以通过增加资源来保证所有对象的线程安全 。 简单来讲就是:要100个人填写信息表 , 我们可以分配100根笔给他们填写 , 人手一根 , 那么填写的速度也将大大增加 。
上面这个代码 , 如果没有同步控制则会出现java.lang.NumberFormatException: multiple points和java.lang.NumberFormatException: For input string: \"\"异常 , 因为SimpleDateFormat不是线程安全的 , 除非加锁控制 。 但是除了加锁我们还有没有其他方法呢 , 答案是有的 , 那就是使用ThreadLocal , 每个线程分配一个SimpleDateFormat 。
为每一个线程分配不同的对象 , 需要在应用层面保证 ThreadLocal 只起到了简单的容器作用
ThreadLocal的实现原理set()方法:
先获取当前线程对象 , 然后通过getMap()方法拿到线程的ThreadLocalMap并将值存入ThreadLocalMap中 。 可以简单把ThreadLocalMap理解为一个Map , 其中key为当前线程对象 , value便是我们所需要的值 。
get()方法:
先获取到当前线程的ThreadLocalMap , 然后通过将自己作为key取得内部的实际数据
如果希望及时回收对象 , 我们应该使用ThreadLocal.remove()方法将这个变量移除 , 否则如果将一些大的对象设置到 ThreadLocal中 , 没有及时回收 , 会造成内存泄漏的可能 。
无锁锁分为乐观锁和悲观锁 , 而无锁就是一种乐观的策略 , 它是使用一种叫比较并交换(CAS , Compare And Swap)的技术来鉴别线程冲突 , 一旦检测到冲突发生 , 就重试当前操作直到没有冲突为止 。
比较并交换CAS的算法过程是:包含三个参数 CAS(VEN) , 其中V表示要更新的变量 , E表示预期值 , N表示新值 。 仅当V值等于E值时 , 才会将V值设置为N值 。 最后返回当前V的真实值 。 当多个线程同时使用
推荐阅读
- Java|计算机专业的本科生,该选择学习Java技术体系还是.NET技术体系
- 挖贝网|销量大幅提高,色如丹2020年上半年净利1202.76万增长47.77%
- 小熊回收站|-链表阻塞队列和数组阻塞队列的异同,Java并发编程
- Java|马化腾登顶中国首富,微信、QQ却都免费使用,腾讯到底咋赚钱的?
- Java|为什么美团骑手总是闯红灯昵
- ifory安福瑞|整体信号提高2倍以上,手机信号不好?教你4个方法
- 手艺人兴哥|运行速度虽然会更快,但用户不太容易感知,手机性能提高10倍
- 阿里巴巴|java三大集合遍历方法
- 编程|JAVA基础-网络编程
- Java|Java中的天使和魔鬼:Unsafe类
