java中的锁

1.互斥同步锁(悲观锁,可重入) 
1)Synchorized 2)ReentrantLock
互斥同步锁也叫做阻塞同步锁,特征是会对没有获取锁的线程进行阻塞。
 

2.非阻塞同步锁 (乐观锁,不可重入)
1) 原子类(CAS),它会先进行资源在工作内存中的更新,然后根据与主存中 旧值的对比来确定在此期间是否有其他线程对共享资源进行了更新,如果旧值与期望值相同,就认为没 有更新,可以把新值写回内存,否则就一直重试直到成功。

3.共享锁read(ReadWriteReentrantLock)

4.排它锁。

5.分段锁。ConcurrentHashMap

6.自旋锁:CAS

7.读写锁。自旋锁原理

ReadWriteLock 读写锁 
为了提高性能,Java 提供了读写锁,在读的地方使用读锁,在写的地方使用写锁,灵活控制,如果没有 写锁的情况下,读是无阻塞的,在一定程度上提高了程序的执行效率。读写锁分为读锁和写锁,多个读锁 不互斥,读锁与写锁互斥,这是由 jvm 自己控制的,你只要上好相应的锁即可。 
读锁 如果你的代码只读数据,可以很多人同时读,但不能同时写,那就上读锁 写锁 
如果你的代码修改数据,只能有一个人在写,且不能同时读取,那就上写锁。总之,读的时候上读锁, 写的时候上写锁! 

共享锁和独占锁 java 
并发包提供的加锁模式分为独占锁和共享锁。
独占锁 
独占锁模式下,每次只能有一个线程能持有锁,ReentrantLock 就是以独占方式实现的互斥锁。独占锁 是一种悲观保守的加锁策略,它避免了读/读冲突,如果某个只读线程获取锁,则其他读线程都只能等 待,这种情况下就限制了不必要的并发性,因为读操作并不会影响数据的一致性。
共享锁 
共享锁则允许多个线程同时获取锁,并发访问 共享资源,如:ReadWriteLock。共享锁则是一种乐观 锁,它放宽了加锁策略,允许多个执行读操作的线程同时访问共享资源。
1. AQS 的内部类 Node 定义了两个常量 SHARED 和 EXCLUSIVE,他们分别标识 AQS 队列中等待线 程的锁获取模式。

2. java 的并发包中提供了 ReadWriteLock,读-写锁。它允许一个资源可以被多个读操作访问,或者 被一个 写操作访问,但两者不能同时进行。 重量级锁(Mutex Lock)

 
Synchronized 是通过对象内部的一个叫做监视器锁(monitor)来实现的。但是监视器锁本质又是依赖 于底层的操作系统的 Mutex Lock 来实现的。而操作系统实现线程之间的切换这就需要从用户态转换到 核心态,这个成本非常高,状态之间的转换需要相对比较长的时间,这就是为什么 Synchronized 效率 低的原因。因此,这种依赖于操作系统 Mutex Lock 所实现的锁我们称之为 “重量级锁”。JDK 中对 Synchronized 做的种种优化,其核心都是为了减少这种重量级锁的使用。
JDK1.6 以后,为了减少获得锁和释放锁所带来的性能消耗,提高性能,引入了“轻量级锁”和 “偏向锁”。

轻量级锁 
锁的状态总共有四种:无锁状态、偏向锁、轻量级锁和重量级锁。 
锁升级 
随着锁的竞争,锁可以从偏向锁升级到轻量级锁,再升级的重量级锁(但是锁的升级是单向的,也就是 说只能从低到高升级,不会出现锁的降级)。
“轻量级”是相对于使用操作系统互斥量来实现的传统锁而言的。但是,首先需要强调一点的是,轻量级 锁并不是用来代替重量级锁的,它的本意是在没有多线程竞争的前提下,减少传统的重量级锁使用产生 的性能消耗。在解释轻量级锁的执行过程之前,先明白一点,轻量级锁所适应的场景是线程交替执行同 步块的情况,如果存在同一时间访问同一锁的情况,就会导致轻量级锁膨胀为重量级锁。

偏向锁 
Hotspot 的作者经过以往的研究发现大多数情况下锁不仅不存在多线程竞争,而且总是由同一线程多次 获得。偏向锁的目的是在某个线程获得锁之后,消除这个线程锁重入(CAS)的开销,看起来让这个线 程得到了偏护。引入偏向锁是为了在无多线程竞争的情况下尽量减少不必要的轻量级锁执行路径,因为 轻量级锁的获取及释放依赖多次 CAS 原子指令,而偏向锁只需要在置换 ThreadID的时候依赖一次 CAS 原子指令(由于一旦出现多线程竞争的情况就必须撤销偏向锁,所以偏向锁的撤销操作的性能损耗必须
小于节省下来的 CAS 原子指令的性能消耗)。上面说过,轻量级锁是为了在线程交替执行同步块时提高 性能,而偏向锁则是在只有一个线程执行同步块时进一步提高性能。

分段锁 
分段锁也并非一种实际的锁,而是一种思想 ConcurrentHashMap 是学习分段锁的好实践 锁优化 
减少锁持有时间 只用在有线程安全要求的程序上加锁 
减小锁粒度 
将大对象(这个对象可能会被很多线程访问),拆成小对象,大大增加并行度,降低锁竞争。
降低了锁的竞争,偏向锁,轻量级锁成功率才会提高。典型的减小锁粒度的案例就是
ConcurrentHashMap。

锁分离 
常见的锁分离就是读写锁 ReadWriteLock,根据功能进行分离成读锁和写锁,这样读读不互斥,读写 互斥,写写互斥,即保证了线程安全,又提高了性能,具体也请查看[高并发 Java 五] JDK 并发包 1。读写分离思想可以延伸,只要操作互不影响,锁就可以分离。比如
LinkedBlockingQueue 从头部取出,从尾部放数据 锁粗化 


通常情况下,为了保证多线程间的有效并发,会要求每个线程持有锁的时间尽量短,即在使用完公共资 源后,应该立即释放锁。但是,凡事都有一个度,如果对同一个锁不停的进行请求、同步和释放,其本 身也会消耗系统宝贵的资源,反而不利于性能的优化 。 
锁消除 
锁消除是在编译器级别的事情。在即时编译器时,如果发现不可能被共享的对象,则可以消除这些对象 的锁操作,多数是因为程序员编码不规范引起。

java中的悲 观锁就是Synchronized,AQS框架下的锁则是先尝试cas乐观锁去获取锁,获取不到,才会转换为悲观 锁,如RetreenLock。

java线程阻塞的代价 
java的线程是映射到操作系统原生线程之上的,如果要阻塞或唤醒一个线程就需要操作系统介入,需要 在户态与核心态之间切换,这种切换会消耗大量的系统资源,因为用户态与内核态都有各自专用的内存 空间,专用的寄存器等,用户态切换至内核态需要传递给许多变量、参数给内核,内核也需要保护好用 户态在切换时的一些寄存器值、变量等,以便内核态调用结束后切换回用户态继续工作。
1. 如果线程状态切换是一个高频操作时,这将会消耗很多CPU处理时间; 2. 如果对于那些需要同步的简单的代码块,获取锁挂起操作消耗的时间比用户代码执行的时间还要 长,这种同步策略显然非常糟糕的。
synchronized会导致争用不到锁的线程进入阻塞状态,所以说它是java语言中一个重量级的同步操纵, 被称为重量级锁,为了缓解上述性能问题,JVM从1.5开始,引入了轻量锁与偏向锁,默认启用了自旋 锁,他们都属于乐观锁。
 

3.无同步方案 
1)可重入代码 在执行的任何时刻都可以中断-重入执行而不会产生冲突。特点就是不会依赖堆上的共享资源 2)ThreadLocal/Volaitile
线程本地的变量,每个线程获取一份共享变量的拷贝,单独进行处理。 3)  线程本地存储
如果一个共享资源一定要被多线程共享,可以尽量让一个线程完成所有的处理操作,比如生产者消费者 模式中,一般会让一个消费者完成对队列上资源的消费。典型的应用是基于请求-应答模式的web服务器 的设计 

读写分离 
CopyOnWriteArrayList 、CopyOnWriteArraySet CopyOnWrite容器即写时复制的容器。通俗的理解是当我们往一个容器添加元素的时候,不直接往当前 容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素 之后,再将原容器的引用指向新的容器。这样做的好处是我们可以对CopyOnWrite容器进行并发的读, 而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读 和写不同的容器。 CopyOnWrite并发容器用于读多写少的并发场景,因为,读的时候没有锁,但是对其进行更改的时候 是会加锁的,否则会导致多个线程同时复制出多个副本,各自修改各自

消除缓存行的伪共享 
除了我们在代码中使用的同步锁和jvm自己内置的同步锁外,还有一种隐藏的锁就是缓存行,它也被称 为性能杀手。 在多核cup的处理器中,每个cup都有自己独占的一级缓存、二级缓存,甚至还有一个共享的三级缓存, 为了提高性能,cpu读写数据是以缓存行为小单元读写的;32位的cpu缓存行为32字节,64位cup的缓 存行为64字节,这就导致了一些问题。 

(虽然只需要一个变量,但是cpu读取会以缓存 行为小单位,将其相邻的变量一起读入),被读入cpu缓存的变量相当于是对主内存变量的一个拷 贝,也相当于变相的将在同一个缓存行中的几个变量加了一把锁,这个缓存行中任何一个变量发生了变 化,当cup-2需要读取这个缓存行时,就需要先将cup-1中被改变了的整个缓存行更新回主存(即使其它 变量没有更改),然后cup-2才能够读取,而cup-2可能需要更改这个缓存行的变量与cpu-1已经更改的 缓存行中的变量是不一样的,所以这相当于给几个毫不相关的变量加了一把同步锁; 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值