synchronized

synchronized的作用:

1)原子性:确保线程互斥的访问同步代码

2)可见性:保证共享变量之间的修改及时可见(通过java内存模型中的‘对一个变量unlock之前,必须要同步到主内存中’)

3)有序性:有效解决重排序问题,即一个unlock操作happen-before后面对同一个锁的lock操作

 

 

synchronized实现同步的基础:java中每一个对象都可以作为锁

对于普通同步方法,锁是当前实例对象

对于静态同步方法,锁是当前类的class对象(一个类,所有类的实例对象共享这一个锁)

对于同步方法块,锁是Synchronized括号里配置的对象(可以认为在方法体上加锁和在方法块中加锁都是锁住的对象)

 

Jvm基于进入和退出Monitor对象来实现代码块同步,两者的实现细节不一样,代码块同步使用monitorenter和monitorexit指令实现的,而方法同步是使用另一种方式是通过一个访问标志判断是否是同步方法(但是方法块的同步同样可以用两个指令来完成)

 

 

synchronized的用法有哪些?一个类中有两个synchronized方法被两个线程锁住,分别调用会阻塞吗?

     加在方法上,加在代码块内,加载静态方法或类上。

     会阻塞,因为本质上都是锁住的该对象,所以同一时刻只能有一个线程访问到同步方法

Java对象头?

synchronized使用的锁是存在java对象头里的,如果对象是数组类型,则虚拟机用3个字宽存储对象头,非数组类型则用两个字宽(一字宽是4字节,32bit)

 

锁的状态:无锁状态,偏向锁,轻量级锁,重量级锁   

锁可以升级但不能够降级

 

偏向锁: 大多数情况下,锁不仅不存在多线程竞争,而且总是有一个线程多次重复获取一个锁,但是获取锁会涉及一些CAS操作耗费时间,为了降低这种情况下获取锁的代价引入了偏向锁。当一个线程获取锁时,会在对象的对象头里用1bit来存储锁偏向的线程id,以后如果该线程进入和退出同步块时就不需要CAS操作了。只是测试一下对象头里是否存储指向该线程的偏向锁,如果测试出来该锁不是偏向锁,则使用CAS操作,如果是偏向锁,则尝试时该偏向锁指向当前线程。偏向锁使用了一种等到竞争出现时才释放锁的机制,如果锁竞争不激烈,则效果很好,因为很有可能是一个线程多次连续获取锁,但是竞争激烈的情况下,偏向锁效率反而更低。偏向锁是默认开启的,如果偏向锁失效后,会升级成为轻量级锁

 

轻量级锁: 线程在执行同步块之间,Jvm会先在当前线程的栈帧中创建用于存储锁记录的空间,并将对象头的mark word复制到锁记录中,先后线程尝试用CAS将对象头中的mark word替换为指向线程的指针,成功则获得锁,失败就自旋等待。解锁时尝试将对象头的mark word替换为线程中的那个记录,成功则不存在竞争,失败就会变成重量级锁。  由于自旋会消耗cpu,为了避免无用的自旋,升级成重量级锁后不能再恢复成轻量级锁了

 

锁的优缺点:

偏向锁:  加锁解锁不需要额外消耗,十分接近于非同步方法的速度         存在竞争时带来额外的锁撤销的消耗

轻量级锁:  竞争线程不阻塞,提高响应速度       自旋会消耗cpu

重量级锁:  不使用自旋,不会消耗cpu     线程阻塞,响应时间缓慢

 

 

synchronized的可重入性?

synchronized是基于原子性的内部锁机制,是可重入的。因此当一个线程调用synchronized方法的同时在其方法内部调用该对象的另一个synchronized方法是允许的

 

总结

1)无论synchronized关键字加载方法上还是代码块内,如果是非静态的,则锁都是对象,如果synchronized作用的是静态方法或类,则锁是类,类的所有对象共用一把锁

1)每个对象只有一把锁与之关联

3)实现同步是需要很大的系统开销作为代价的,甚至可能造成死锁

4)当一个线程访问对象的synchronized(this)同步代码块时,其他线程任然可以访问该对象中的非synchronized方法(因为只有synchronized修饰的方法或对象,线程来获取时才会在意是否阻塞,普通方法不用)

5)synchronized关键字不能继承,如果父类中的某个方法是 synchronized的,子类覆盖该方法而没有使用synchronized,则此时该方法是非同步的,当然可以在子类的该方法中调用父类的synchronized方法

6)定义接口方法时不能使用synchronized关键字,构造方法不能使用synchronized方法(但是可以用synchronized代码块)

 

 

可重入锁的实现原理?

重入锁实现可重入性原理或机制是:每一个锁关联一个线程持有者和计数器,当计数器为 0 时表示该锁没有被任何线程持有,那么任何线程都可能获得该锁而调用相应的方法;当某一线程请求成功后,JVM会记下锁的持有线程,并且将计数器置为 1;此时其它线程请求该锁,则必须等待;而该持有锁的线程如果再次请求这个锁,就可以再次拿到这个锁,同时计数器会递增;当线程退出同步代码块时,计数器会递减,如果计数器为 0,则释放该锁

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值