「java锁膨胀」java轻量级锁膨胀时机
今天给各位分享java锁膨胀的知识,其中也会对java轻量级锁膨胀时机进行解释,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!
本文目录一览:
- 1、synchronized锁的优化
- 2、java线程锁有几种
- 3、Java锁有哪些种类,以及区别
- 4、java偏向锁,轻量级锁与重量级锁为什么会相互膨
- 5、多线程高并发之Synchronized锁及其膨胀
synchronized锁的优化
Java SE 1.6为了减少获得锁和释放锁带来的性能消耗,引入了“偏向锁”和“轻量级锁”。锁一共有四个状态,级别从低到高依次是:无锁、偏向锁、轻量级锁和重量级锁。这个几个状态会随着竞争情况逐渐升级,锁可以升级但不能降级。
Java HotSpot 虚拟机中,每个对象都有对象头(包括 class 指针和 Mark Word)。Mark Word 平时存储这个对象的 哈希码 、 分代年龄 ,当加锁时,这些信息就根据情况被替换为 标记位 、 线程锁记录指 针 、 重量级锁指针 、 线程ID 等内容。
1.偏向锁
HotSpot的作者发现,大多数情况下,锁不紧不存在多线程竞争,而且总是被同一个线程多次获得,为了让线程获取锁的代价更低引入了偏向锁。
当一个线程获取锁时,会在对象头和栈帧的锁记录里存储偏向锁的线程id,以后线程再进入和退出锁时,不需要CAS操作来加锁和解锁,只需测试下对象头的Mark Word里是否存储着当前线程id。
2.轻量级锁
当一个线程虽然有多个线程访问,但是访问时间是错开的,这种情况就可以用轻量级锁优化。
3.锁膨胀
如果在尝试加轻量级锁的过程中,CAS操作无法成功,说明这时已经有其他线程加上了轻量级锁,也就是说存在了锁竞争,这时轻量级锁要膨胀成重量级锁。
4.自旋锁
重量级锁竞争的时候,还可以使用自旋来进行优化,如果当前线程自旋成功(即这时候持锁线程已经退出了同步块,释放了锁),这时当前线程就可以避免阻塞。
在 Java 6 之后自旋锁是自适应的,比如对象刚刚的一次自旋操作成功过,那么认为这次自旋成功的可能性会高,就多自旋几次;反之,就少自旋甚至不自旋。
5.其他优化
java线程锁有几种
1、自旋锁
2、自旋锁的其他种类
3、阻塞锁
4、可重入锁
5、读写锁
6、互斥锁
7、悲观锁
8、乐观锁
9、公平锁
10、非公平锁
11、偏向锁
12、对象锁
13、线程锁
14、锁粗化
15、轻量级锁
16、锁消除
17、锁膨胀
18、信号量
Java锁有哪些种类,以及区别
一、公平锁/非公平锁
公平锁是指多个线程按照申请锁的顺序来获取锁。
非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。有可能,会造成优先级反转或者饥饿现象。
对于Java ReentrantLock而言,通过构造函数指定该锁是否是公平锁,默认是非公平锁。非公平锁的优点在于吞吐量比公平锁大。
对于Synchronized而言,也是一种非公平锁。由于其并不像ReentrantLock是通过AQS的来实现线程调度,所以并没有任何办法使其变成公平锁。
二、可重入锁
可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁。说的有点抽象,下面会有一个代码的示例。
对于Java ReentrantLock而言, 他的名字就可以看出是一个可重入锁,其名字是Re entrant Lock重新进入锁。
对于Synchronized而言,也是一个可重入锁。可重入锁的一个好处是可一定程度避免死锁。
synchronized void setA() throws Exception{
Thread.sleep(1000);
setB();
}
synchronized void setB() throws Exception{
Thread.sleep(1000);
}
上面的代码就是一个可重入锁的一个特点,如果不是可重入锁的话,setB可能不会被当前线程执行,可能造成死锁。
三、独享锁/共享锁
独享锁是指该锁一次只能被一个线程所持有。
共享锁是指该锁可被多个线程所持有。
对于Java
ReentrantLock而言,其是独享锁。但是对于Lock的另一个实现类ReadWriteLock,其读锁是共享锁,其写锁是独享锁。
读锁的共享锁可保证并发读是非常高效的,读写,写读 ,写写的过程是互斥的。
独享锁与共享锁也是通过AQS来实现的,通过实现不同的方法,来实现独享或者共享。
对于Synchronized而言,当然是独享锁。
四、互斥锁/读写锁
上面讲的独享锁/共享锁就是一种广义的说法,互斥锁/读写锁就是具体的实现。
互斥锁在Java中的具体实现就是ReentrantLock
读写锁在Java中的具体实现就是ReadWriteLock
五、乐观锁/悲观锁
乐观锁与悲观锁不是指具体的什么类型的锁,而是指看待并发同步的角度。
悲观锁认为对于同一个数据的并发操作,一定是会发生修改的,哪怕没有修改,也会认为修改。因此对于同一个数据的并发操作,悲观锁采取加锁的形式。悲观的认为,不加锁的并发操作一定会出问题。
乐观锁则认为对于同一个数据的并发操作,是不会发生修改的。在更新数据的时候,会采用尝试更新,不断重新的方式更新数据。乐观的认为,不加锁的并发操作是没有事情的。
从上面的描述我们可以看出,悲观锁适合写操作非常多的场景,乐观锁适合读操作非常多的场景,不加锁会带来大量的性能提升。
悲观锁在Java中的使用,就是利用各种锁。
乐观锁在Java中的使用,是无锁编程,常常采用的是CAS算法,典型的例子就是原子类,通过CAS自旋实现原子操作的更新。
六、分段锁
分段锁其实是一种锁的设计,并不是具体的一种锁,对于ConcurrentHashMap而言,其并发的实现就是通过分段锁的形式来实现高效的并发操作。
我们以ConcurrentHashMap来说一下分段锁的含义以及设计思想,ConcurrentHashMap中的分段锁称为Segment,它即类似于HashMap(JDK7与JDK8中HashMap的实现)的结构,即内部拥有一个Entry数组,数组中的每个元素又是一个链表;同时又是一个ReentrantLock(Segment继承了ReentrantLock)。
当需要put元素的时候,并不是对整个hashmap进行加锁,而是先通过hashcode来知道他要放在那一个分段中,然后对这个分段进行加锁,所以当多线程put的时候,只要不是放在一个分段中,就实现了真正的并行的插入。
但是,在统计size的时候,可就是获取hashmap全局信息的时候,就需要获取所有的分段锁才能统计。
分段锁的设计目的是细化锁的粒度,当操作不需要更新整个数组的时候,就仅仅针对数组中的一项进行加锁操作。
七、偏向锁/轻量级锁/重量级锁
这三种锁是指锁的状态,并且是针对Synchronized。在Java
5通过引入锁升级的机制来实现高效Synchronized。这三种锁的状态是通过对象监视器在对象头中的字段来表明的。
偏向锁是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁。降低获取锁的代价。
轻量级锁是指当锁是偏向锁的时候,被另一个线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,提高性能。
重量级锁是指当锁为轻量级锁的时候,另一个线程虽然是自旋,但自旋不会一直持续下去,当自旋一定次数的时候,还没有获取到锁,就会进入阻塞,该锁膨胀为重量级锁。重量级锁会让其他申请的线程进入阻塞,性能降低。
八、自旋锁
在Java中,自旋锁是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU。
典型的自旋锁实现的例子,可以参考自旋锁的实现
java偏向锁,轻量级锁与重量级锁为什么会相互膨
不知道你所说的轻量级锁重量级锁代表什么?
锁多Java程序只是多线程应用在现代cpu架构下访问共享数据保护代码临界区的一种安全手段。是不是看书看多了别人忽悠懵了?
使用多个锁,由于竞争条件,产生死锁,或锁,使用锁的约定顺序等等这些问题是由操作系统的锁实现最终决定,是必要结果。就是你说的锁的相互影响!理解了大多情况是可避免的,除非业务特别复杂考虑不周全。实际使用中没书上写的那么复杂。
多线程高并发之Synchronized锁及其膨胀
在并发编程中,synchronized锁因其使用简单,在线程间同步被广泛应用。下面对其原理及锁升级过程进行探究。
当实例方法被synchronized修饰时,通过当前实例调用此方法的所有线程共用一把锁,不同对象调用此方法线程间互不影响。
当使用synchronized锁修饰实例方法,锁添加在当前类的实例上,有多少个实例可添加多少把锁。
修饰代码块比修饰方法颗粒度更小。当实例方法代码块被synchronized修饰时,通过当前实例调用此方法的所有线程共用一把锁,不同对象调用此方法线程间互不影响。
当使用synchronized锁修饰代码块,锁添加在当前类的实例上,有多少个实例可添加多少把锁。
当静态方法被synchronized修饰时,整个JVM所有调用此方法的线程均受同一个锁的约束。
当使用synchronized锁修饰静态方法,锁添加在当前类的类对象上,最多添加一把锁。
Java 8所使用的synchronized锁是经过优化后的,存在偏向锁、轻量级锁、重量级锁等状态。
线程间不存在锁的竞争行为,至多只有一个线程有获取锁的需求,常见场景为单线程程序。
判断是不是偏向锁的标识是查看调用此方法的线程是否有且仅有一个。
在多线程编程里,被锁修饰的方法仅被单一线程调用几乎不存在,因此偏向锁比较鸡肋:如果能够明确单一线程调用目标方法,使用无锁编程更为合适。
无锁与偏向锁的性能差异非常接近,几乎可以忽略不计。
线程间存在锁的伪竞争行为,即同一时刻绝对不会存在两个线程申请获取锁,各线程尽管都有使用锁的需求,但是是交替使用锁。
当有两个及以上线程调用被锁修饰的方法时,那么至少能确定是轻量级锁。
轻量级锁由于同一时刻不存在两个线程互相竞争锁,因此不存在线程阻塞-唤醒的上下文切换,因此性能相对重量级锁要高很多。
线程间存在锁的实质性竞争行为,线程间都有获取锁的需求,但是时间不可交错,互斥锁的阻塞等待。
当能够肯定至少有两个及以上线程调用被锁修饰的方法时,线程调用方法是随机的,那么大概率是重量级锁。
重量级锁由于涉及到线程阻塞-唤醒的上下文切换,造成相比较与无锁状态,效率低很多。
synchronized锁是非公平锁,没有FIFO队列机制保障竞争锁的线程一定有几率获得锁。
synchronized锁是可重入锁,可重入意味着嵌套调用不会产生死锁问题。
synchronized锁是一种悲观锁,通过加锁实现线程间同步。
在多线程环境下,如果使用synchronized锁,那么大概率会升级到重量级锁。偏向锁和轻量级锁非刻意为之,很难存在,更大的意义是对比帮助理解重量级锁的性能。
重量级锁尽管会对性能产生很大影响,但是依旧是解决线程间同步的有效手段。
当被锁修饰的方法或者代码块执行时间较长时,选用基于线程阻塞-唤醒切换上下文的方式进行线程同步效率相对较高。
当被锁修饰的方法或者代码块执行时间较短时,应选用其它替代锁,比如自旋锁等。
在实际多线程场景开发中,synchronized锁大概率会升级到重量级锁,因其单向升级的特点,重量级状态的synchronized锁可能会对实际业务的并发产生不利影响,手动选用其它锁可能会更合适。
synchronized锁仅可用于解决同一进程内不同线程间同步,对于分布式项目跨进城线程同步依赖于分布式锁,synchronized锁更多的意义是理解锁的过程。
java锁膨胀的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于java轻量级锁膨胀时机、java锁膨胀的信息别忘了在本站进行查找喔。
发布于:2022-12-09,除非注明,否则均为
原创文章,转载请注明出处。