多线程学习日记:synchronized

一.同步器的意义

多线程编程中,有可能会出现多个线程同时访问同一个共享、可变资源的情况,这个资源我们称之其为临界资源;这种资源可能是:
对象、变量、文件等。
共享:资源可以由多个线程同时访问
可变:资源可以在其生命周期内被修改
引出的问题:
由于线程执行的过程是不可控的,所以需要采用同步机制来协同对对象可变状态的访问!
如何解决线程并发安全问题?
实际上,所有的并发模式在解决线程安全问题时,采用的方案都是 序列化访问临界资源 。即在同一时刻,只能有一个线程访问临
界资源,也称作 同步互斥访问
Java 中,提供了两种方式来实现同步互斥访问: synchronized Lock
同步器的本质就是加锁
加锁目的: 序列化访问临界资源 ,即同一时刻只能有一个线程访问临界资源( 同步互斥访问 )
不过有一点需要区别的是:当多个线程执行一个方法时,该方法内部的局部变量并不是临界资源,因为这些局部变量是在每个线程的
私有栈中,因此不具有共享性,不会导致线程安全问题。

二.synchronized原理详解

synchronized内置锁是对象锁(锁定的是对象而非引用),可以用来实现对临界资源的互斥同步访问,可重入

jdk<1.6 

synchronized基于JVM内置锁实现,通过内部对象Monitor实现,基于进入和推出Monitor对象实现代码和方法块同步,监视器锁的实现依赖底层操作系统的Mutex lock(互斥锁),是重量级锁,性能较低

jdk>=1.6

做了优化,有一个锁的膨胀机制,并发性能和Lock基本持平。synchronized关键字被编译成字节码后会被翻译成monitorenter和monitorexit两条指令分别在同步代码块的起始和结束位置

 每个同步对象都有一个自己的Monitor(监视器锁),加锁过程如下图所示:

Monitor监视器锁
任何一个对象都有一个Monitor与之关联,当且一个Monitor被持有后,它将处于锁定状态 。Synchronized在JVM里的实现都是
基于进入和退出Monitor对象来实现方法同步和代码块同步 ,虽然具体实现细节不一样,但是都可以通过成对的MonitorEnter和MonitorExit指令来实现。
monitorenter :每个对象都是一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:
a. 如果monitor的进入数为0 ,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor
的所有者;
b. 如果线程已经占有该monitor ,只是重新进入,则进入monitor的进入数加1;
c. 如果其他线程已经占用了monitor ,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝
试获取monitor的所有权;
monitorexit :执行monitorexit的线程必须是objectref所对应的monitor的所有者。 指令执行时,monitor的进入数减
1,如果减1后进入数为0,那线程退出monitor,不再是这个monitor的所有者 。其他被这个monitor阻塞的线程可以尝试去获取这个 monitor 的所有权。
monitorexit,指令出现了两次,第1次为同步正常退出释放锁;第2次为发生异步退出释放锁
通过上面两段描述,我们应该能很清楚的看出Synchronized的实现原理, Synchronized的语义底层是通过一个monitor的对象来 完成,其实wait/notify等方法也依赖于monitor对象 ,这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法, 否则
会抛出java.lang.IllegalMonitorStateException的异常的原因

三.对象的内存布局

HotSpot虚拟机中,对象在内存中存储的布局可以分为三块区域:对象头(Header)、实例数据(
Instance Data)和对齐填充 (Padding)。
对象头:比如 hash码,对象所属的年代,对象锁,锁状态标志,偏向锁(线程)ID,偏向时间,数组长度(数组对象)
等。 Java对象头一般占有2个机器码 (在32位虚拟机中,1个机器码等于4字节,也就是32bit,在64位虚拟机中,1个机器码
是8个字节,也就是64bit),但是 如果对象是数组类型,则需要3个机器码,因为JVM虚拟机可以通过Java对象的元数据信
息确定Java对象的大小 ,但是无法从数组的元数据来确认数组的大小,所以用一块来记录数组长度。
实例数据: 存放类的属性数据信息,包括父类的属性信息;
对齐填充: 由于虚拟机要求 对象起始地址必须是8字节的整数倍 。填充数据不是必须存在的,仅仅是为了字节对齐;

四.锁的膨胀过程

1.无锁->轻量级锁

2.无锁->重量级锁

 线程1占有锁时间较长,线程2自旋10次升级成重量锁

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 我可以简单解释一下多线程synchronized的含义。synchronized 是 Java 语言中的一个关键字,它修饰的代码块,表示当前线程独占对象,其他线程无法对这段代码进行访问,也就是只有一个线程可以执行这段代码,其他线程只能等待。这样做的目的是为了保证共享资源的完整性和一致性。 ### 回答2: 多线程synchronized是一种用于实现线程安全的技术。在多线程编程中,当多个线程同时访问共享资源时,可能会出现数据不一致或产生竞态条件等问题。为了解决这些问题,可以使用synchronized关键字对关键代码块或方法进行同步。 synchronized关键字可以用于修饰代码块或方法。当修饰代码块时,需要指定对象作为锁,即synchronized(obj){},这样只有获取到锁的线程才能执行代码块内的内容,其他线程需要等待。而当修饰方法时,表示整个方法是同步的,相当于synchronized(this){}。 通过synchronized关键字,可以保证在同一时间只有一个线程可以执行被锁定的代码块或方法,从而避免了并发访问共享资源时的冲突。当一个线程执行完synchronized代码块或方法后,会释放锁,其他线程才能获取到锁并执行相应的代码。 synchronized还具有可重入性,即一个线程在获取到锁后,可以再次获取到该锁,而不会造成死锁。这是因为synchronized是基于持有锁的机制,当线程再次进入被synchronized修饰的代码块或方法时,会判断当前线程是否已经持有锁,如果是则可以继续执行,否则需要等待获取锁。 然而,synchronized也存在一些限制和局限性。首先,synchronized只能保证同一时间只能有一个线程执行被锁定的代码块或方法,不能满足多个线程同时访问的需求。其次,如果一个线程获取到锁后进入了死循环或长时间的阻塞操作,会导致其他线程一直等待无法获得锁,从而造成线程的饥饿问题。 总的来说,多线程synchronized是一种常用的线程同步技术,通过保证同一时间只有一个线程访问共享资源,从而避免了数据不一致和竞态条件等问题。然而,在使用synchronized时需要注意避免死锁和饥饿问题的发生。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长方体移动工程师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值