java中的锁---synchronized

参考:
https://blog.csdn.net/javazejian/article/details/72828483
https://cloud.tencent.com/developer/article/1465413
https://blog.csdn.net/javazejian/article/details/72828483
https://www.jianshu.com/p/36eedeb3f912

synchronized是Java中的关键字,是一种同步锁,synchronized 是非公平锁,可重入锁。
synchronized关键字最主要有以下3种应用方式,下面分别介绍
修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁
修饰静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象(Class对象)的锁
修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。

synchronized锁的实现:
synchronized有两种形式上锁,一个是对方法上锁,一个是构造同步代码块。
他们的底层实现其实都一样,在进入同步代码之前先获取锁,获取到锁之后锁的计数器+1,同步代码执行完锁的计数器-1,
如果获取失败就阻塞式等待锁的释放。只是他们在同步块识别方式上有所不一样,
从class字节码文件可以表现出来(javap -v xxx.class),同步方法的字节码码里flags里面多了一个 ACC_SYNCHRONIZED 来告诉JVM这是一个同步方法,
同步代码块的字节码中是有由monitorenter指令进入,然后monitorexit释放锁

java虚拟机对synchronized的优化(jdk6)
synchronized锁有四种状态,无锁状态、偏向锁、轻量级锁和重量级锁。
锁膨胀:随着锁的竞争,锁可以从偏向锁升级到轻量级锁,再升级的重量级锁,但是锁的升级是单向的,也就是说只能从低到高升级,不会出现锁的降级.
偏向锁、轻量级锁、重量级锁适用于不同的并发场景:
偏向锁:无实际竞争,且将来只有第一个申请锁的线程会使用锁。
轻量级锁:无实际竞争,多个线程交替使用锁;允许短时间的锁竞争。
重量级锁:有实际竞争,且锁竞争时间长。

偏向锁: 在大多数情况下,锁不仅不存在多线程竞争,而且总是由同一线程多次获得,因此为了减少同一线程获取锁(会涉及到一些CAS操作,耗时)的代价而引入偏向锁。
偏向锁的核心思想是,如果一个线程获得了锁,那么锁就进入偏向模式,此时会在对象头和栈帧中的锁记录里存储锁偏向的线程ID,
以后该线程在进入和退出同步块时不需要进行CAS操作来加锁和解锁,只需简单地测试一下对象头的Mark Word里是否存储着指向当前线程的偏向锁
如果测试成功,表示线程已经获得了锁。。如果测试失败,则需要再测试一下Mark Word中偏向锁的标识是否设置成1(表示当前是偏向锁):
如果没有设置,则使用CAS竞争锁;如果设置了,则尝试使用CAS将对象头的偏向锁指向当前线程
当其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁

轻量级锁: 当存在第二个线程申请同一个锁对象时,锁会自动升级成轻量级锁,
使用轻量级锁时,不需要申请互斥量,仅仅将Mark Word中的部分字节CAS更新指向线程栈中的Lock Record,如果更新成功,
则轻量级锁获取成功,记录锁状态为轻量级锁;否则,说明已经有线程获得了轻量级锁,目前发生了锁竞争(不适合继续使用轻量级锁),接下来膨胀为重量级锁。

重量级锁: 重量级锁是我们常说的传统意义上的锁,其利用操作系统底层的同步机制去实现Java中的线程同步.

synchronized如何实现原子性,可见性(缓存一致性),有序性,可重入性的:
原子性: synchronized修饰的类或对象的所有操作都是原子的,因为在执行操作之前必须先获得类或对象的锁,直到执行完才能释放,这中间的过程无法被中断(除了已经废弃的stop()方法),即保证了原子性。
注意!面试时经常会问比较synchronized和volatile,它们俩特性上最大的区别就在于原子性,volatile不具备原子性。
可见性 :synchronized和volatile都具有可见性,其中synchronized对一个类或对象加锁时,一个线程如果要访问该类或对象必须先获得它的锁,而这个锁的状态对于其他任何线程都是可见的,并且在释放锁之前会将对变量的修改刷新到主存当中,保证资源变量的可见性,如果某个线程占用了该锁,其他线程就必须在锁池中等待锁的释放。
有序性: synchronized和volatile都具有有序性,Java允许编译器和处理器对指令进行重排,但是指令重排并不会影响单线程的顺序,它影响的是多线程并发执行的顺序性 synchronized保证了每个时刻都只有一个线程访问同步代码块,也就确定了线程执行同步代码块是分先后顺序的,保证了有序性。

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Javasynchronized升级是为了提高多线程并发执行的性能和效率。它通过在的使用过程进行优化和升级来实现。 在Javasynchronized的升级主要涉及三个层面:Java层面、字节码层面和JVM层面(对象头)。 在Java层面上,synchronized的升级包括以下几种状态: 1. 无状态:多个线程可以同时进入临界区,没有互斥的限制; 2. 偏向状态:当只有一个线程访问临界区时,偏向可以减少的竞争; 3. 轻量级状态:多个线程竞争同一个时,会升级为轻量级,通过CAS操作来实现快速的加和解; 4. 重量级状态:多个线程竞争同一个时,会升级为重量级,使用操作系统的互斥量来实现线程的阻塞和唤醒[1]。 在字节码层面上,synchronized同步代码块的升级实际上是通过字节码指令来实现的。当进入同步代码块时,会通过monitorenter指令获取,在退出同步代码块时,会通过monitorexit指令释放。这些指令可以保证临界区的原子性和互斥性,从而实现线程的同步。 在JVM层面上,synchronized的升级是通过对象头的标记位来实现的。对象头的标记位包括了标志位、线程ID和指向记录的指针。通过这些标记位,JVM可以判断的状态和竞争情况,从而进行的升级和降级。 总结来说,Javasynchronized升级是为了提高多线程并发执行的性能和效率。它通过在不同层面上对进行优化和升级来实现线程的同步和互斥,从而保证临界区的原子性和正确性。这些优化和升级包括了无状态、偏向状态、轻量级状态和重量级状态。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值