【Java se】synchronized底层的锁

目录

锁的分类

无锁

偏向锁 

轻量锁

重量锁

不同锁的优缺点

对象头


锁的分类

         synchd锁有四种状态:无锁、偏向锁、轻量锁、重量锁。使用synchd后,随着线程的不断竞争锁会升级但不会降级。但偏向锁可以被置为无锁状态(锁撤销)。

 

没有开启偏向锁的情况下:

  • 一个对象没有被作为锁对象,处于无锁状态
  • 一个对象被一个线程获取作为锁对象,处于轻量级锁状态
  • 一个线程已经持有了该锁对象,其他线程来争用,处于重量级锁状态。

开启偏向锁的情况下:

  • 一个对象没有被作为锁对象,处于无锁可偏向状态。(对象头中没有记录线程ID)
  • 一个对象被一个线程作为锁对象,处于轻量级锁状态。(对象头中记录了线程ID)
  • 一个对象被一个线程作为锁对象,释放锁对象后(但是该线程没有消亡时),其他线 程再获取该锁对象,则处于轻量级锁状态。
  • 一个对象被一个线程作为锁对象,没有释放锁,其他线程也要获取该锁对象,就处 于重量级锁状态。

 

无锁

        一个对象如果没有被任何线程当作锁去使用,就是无锁状态。

无锁——>偏向锁

        jvm启动偏向锁默认打开,但是偏向锁启动是有延时的(4000ms)

        jvm参数-XX:BiasedLockingStartupDelay=4000

        将数值设置为0则取消延迟

public static void main(String[] args) throws InterruptedException {
		A a = new A();
//		ClassLayout pi = ClassLayout.parseInstance(obj);
//		String str = pi.toPrintable();
		
		String str = ClassLayout.parseInstance(a).toPrintable();
		System.out.println(str);//01	无锁状态
}

偏向锁 

        大多数时候是不存在锁竞争的,经常是一个线程在持有锁,由于每次竞争锁会有很大的性能开销,为了降低锁的代价,从而引入了偏向锁。

偏向锁——>轻量锁

A线程访问代码块获取到锁对象之后,会在java对象头和栈帧记录偏向锁的请求线程threadid,因为偏向锁不会主动释放,所以之后A线程再次获取锁的时候,需要比较A线程的threadid是否和对象头中的线程id—致。

  • —致:锁重入A线程继续获取锁,无需使用CAS加锁、解锁
  • 不一致:说明有其它线程B来竞争锁对象,由于偏向锁不会主动释放锁对象,对象头存储的是线程A的threadid,那么需要对象头记录线程A是否存活
  1. 如果没有存活,那么锁对象被重置为无锁状态(锁撤销),线程B设置偏向锁
  2. 如果存活,立即查找线程A的栈帧信息,如果线程A还需要继续持有锁,那么暂停线程A (stw),撤销A的偏向锁升级为轻量级锁,如果不需要继续持有锁,锁对象重置为无锁状态,锁偏向线程B
public static void main(String[] args) throws InterruptedException {
		A a = new A();
		String str = ClassLayout.parseInstance(a).toPrintable();
		System.out.println(str);//101  无锁可偏向
		
		new Thread() {
			public void run() {
				synchronized(a) {
					try {
						Thread.sleep(500);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				
				/*
				 * 	显示的让当前线程不结束
				 * 	如果结束了,下一个线程的id和刚才这个线程的id是一样的。
				 */
				try {
					Thread.sleep(100000000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}.start();
		Thread.sleep(100);
		str = ClassLayout.parseInstance(a).toPrintable();
		System.out.println(str);//101  无锁可偏向
		
		Thread.sleep(1000);//保证上一个线程已经把任务执行完毕了
		new Thread() {
			public void run() {
				synchronized(a) {
					try {
						Thread.sleep(500);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}.start();
		Thread.sleep(100);
		str = ClassLayout.parseInstance(a).toPrintable();
		System.out.println(str);
	}

轻量锁

        轻量锁考虑的是竞争锁的线程不是很多,且持有锁的时间短的场景。因为线程阻塞需要CPU从用户态切换到内核态,如果线程刚阻塞不久,锁就释放了,那这个代价有点大。所以干脆不阻塞这个线程,让它进行自旋等待锁释放。

轻量锁——>重量锁

A线程获得轻量级锁之后,会把锁对象的对象头Markword复制一份到线程A的栈帧中创建用于存储锁记录的空间(DisplacedMarkword),然后使用CAS自旋把对象头中的内容替换为A线程存储锁记录的地址。

如果A线程在复制对象头的过程中(cas替换前),C线程也获取锁,复制了锁对象头到C线程的锁记录空间,在执行CAS自旋做对象头内容替换的时候,发现A线程已经更改了对象头,则C线程替换对象头内容失败,尝试使用自旋锁等待A线程释放锁。

自旋要消耗CPU会影响性能,所以不能无限制的自旋下去,因此自旋次数有限制,如果自旋次数到了,A线程还没有释放锁,C线程在自旋等待,D线程又进来竞争锁,这个时候轻量级锁会升级为重量级锁。重量级锁把除A线程之外的,前来竞争锁的线程全部阻塞,防止CPU空转。

new Thread(){
			public void run() {
				//把a作为锁对象
				synchronized(a) {
					try {
						Thread.sleep(500);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}.start();
        Thread.sleep(10);
		str = ClassLayout.parseInstance(a).toPrintable();
		System.out.println(str);//00	轻量级锁

重量锁

        重量级锁就是一个悲观锁了,但是其实不是最坏的锁,因为升级到重量级锁,是因为线程占用锁的时间长(自旋获取失败),锁竞争激烈的场景,在这种情况下,让线程进入阻塞状态,进入阻塞队列,能减少cpu消耗。所以说在不同的场景使用最佳的解决方案才是最好的技术。

new Thread(){
			public void run() {
				//把a作为锁对象
				synchronized(a) {
					try {
						Thread.sleep(500);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}.start();
		Thread.sleep(10);
		//这个时候第一个线程正在使用a这个锁,第二个线程也要去获取a这个锁
		str = ClassLayout.parseInstance(a).toPrintable();
		System.out.println(str);//10	重量级锁


 

不同锁的优缺点

锁状态优点缺点适用场景
偏向锁加锁、解锁没有额外消耗。和执行非同步方法仅有纳秒级的差距如果线程间存在锁竞争,会有额外锁撤销的性能消耗基本没有线程竞争,一个线程访问同步的场景
轻量锁竞争线程不会阻塞,适用cas自旋,提高了程序的响应速度如果无法获取到锁,长时间自旋会消耗CPU性能锁持有时间短,追求响应速度
重量锁线程竞争不使用自旋,不会导致cpu空转线程阻塞,响应慢追求吞吐量,锁持有时间长

对象头

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值