一、设计同步器的意义

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

二、synchronized锁原理解析

synchronized内置锁是一种对象锁(锁的是对象而非引用),作用粒度是对象,可以用来实现对临界资源的同步互斥访问,是可重入的。加锁的方式:
1、同步实例方法,锁是当前实例对象
2、同步类方法,锁是当前类对象
3、同步代码块,锁是括号里面的对象
 

三、synchronized底层原理

synchronized是基于JVM内置锁实现,通过内部对象Monitor(监视器锁)实现,java中每个对象都有一个内置对象Monitor(监视器锁)。基于进入与退出Monitor对象实现方法与代码块同步,监视器锁的实现依赖底层操作系统的Mutex lock(互斥锁)实现,它是一个重量级锁性能较低。当然,JVM内置锁在1.5之后版本做了重大的优化,如锁粗化(LockCoarsening)、锁消除(Lock Elimination)、轻量级锁(LightweightLocking)、偏向锁(Biased Locking)、适应性自旋(Adaptive Spinning)等技术来减少锁操作的开销,,内置锁的并发性能已经基本与Lock持平。
synchronized关键字被编译成字节码后会被翻译成monitorenter 和monitorexit 两条指令分别在同步块逻辑代码的起始位置与结束位置。

既然每个对象都有一个内置对象Monitor,我们如何看到这内置对象Monitor,以及这个对象锁都有哪些内容呢,这个我们就需要查看jvm源码,
查看jdk源码  \openjdk\hotspot\src\share\vm\runtime   在这个目录下找到objectMonitor.hpp(c++编写)查看jdk原代码
ObjectMonitor() {
_header = NULL; //对象头
_count = +++---; //记录加锁次数,锁重入时用到
_waiters = , //当前有多少处于wait状态的thread
_recursions = ;
_object = NULL;
_owner = ; //指向持有ObjectMonitor对象的线程
_WaitSet = NULL; //处于wait状态的线程,会被加入到_WaitSet
_WaitSetLock = ;
_Responsible = NULL ;
_succ = NULL ;
_cxq = NULL ;
FreeNext = NULL ;
_EntryList = NULL ;//处于等待加锁block状态的线程,会被加入到该列表
_SpinFreq = ;
_SpinClock = ;
OwnerIsThread = ;
_previous_owner_tid = ;
}

我们这个每个对象都有内置对象Monitor,那这个内置对象存在我们对象的哪里呢,这就需要了解在java里面我们的对象的内存划分,看下图:

认识对象的内存结构:

1、对象头:比如 hash码,对象所属的年代,对象锁,锁状态标志,偏向锁(线程)ID,偏向时间,数组长度(数组对象)等

2、对象实际数据:即创建对象时,对象中成员变量,方法等

3、对齐填充:对象的大小必须是8字节的整数倍

可见,和锁相关的信息比如锁的次数,锁的状态都在都对象头里面存储,另外MetaData(元数据指针)存的是指向类对象信息,所以每个对象getClass可以获取类对象。

jvm对象整体加锁过程:

四、逃逸分析

面试问题:实例对象内存中存储在哪

答:如果实例对象存储在堆区时:实例对象内存存在堆区,实例的引用存在栈上,实例的元数据class存在方法区或者元空间

Object实例对象一定是存在堆区的吗

不一定,如果实例对象没有线程逃逸行为

使用逃逸分析,编译器可以对代码做如下优化:
一、同步省略。如果一个对象被发现只能从一个线程被访问到,那么对于这个对象的操作可以不考虑同步。
二、将堆分配转化为栈分配。如果一个对象在子程序中被分配,要使指向该对象的指针永远不会逃逸,对象可能是栈分配的候选,而不是堆分配。
三、分离对象或标量替换。有的对象可能不需要作为一个连续的内存结构存在也可以被访问到,那么对象的部分(或全部)可以不存储在内存,而是存储在CPU寄存器中。
是不是所有的对象和数组都会在堆内存分配空间?
不一定
在Java代码运行时,通过JVM参数可指定是否开启逃逸分析,-XX:+DoEscapeAnalysis : 表示开启逃逸分析 -­XX:­-DoEscapeAnalysis : 表示关闭逃逸分析 从jdk 1.7开始已经默认开始逃逸分析,如需关闭,需要指定-­XX:-­DoEscapeAnalysis。
总结一句话:java1.7默认开启了逃逸分析,在开启逃逸分析的情况下,如果一个对象只能够在某一个线程被访问到而不会被其他线程访问,那么这个对象是有可能存在栈内存的,所以不是所有的的对象都在堆内存。
 

并发编程之synchronized锁(一)的更多相关文章

  1. Java并发编程之synchronized关键字

    整理一下synchronized关键字相关的知识点. 在多线程并发编程中synchronized扮演着相当重要的角色,synchronized关键字是用来控制线程同步的,可以保证在同一个时刻,只有一个 ...

  2. Java 多线程并发编程之 Synchronized 关键字

    synchronized 关键字解析 同步锁依赖于对象,每个对象都有一个同步锁. 现有一成员变量 Test,当线程 A 调用 Test 的 synchronized 方法,线程 A 获得 Test 的 ...

  3. 并发编程之synchronized(二)------jvm对synchronized的优化

    一.锁的粗化 看如下代码 public class Test { StringBuffer stb = new StringBuffer(); public void test1(){ //jvm的优 ...

  4. 高并发编程之synchronized

    一.什么是线程? 线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元.一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成.另外,线程 ...

  5. 并发编程之Java锁

    一.重入锁 锁作为并发共享数据,保证一致性的工具,在JAVA平台有多种实现(如 synchronized(重量级) 和 ReentrantLock(轻量级)等等 ) .这些已经写好提供的锁为我们开发提 ...

  6. Java并发编程之synchronized

    在Java编程中,为了保证线程安全,有3种不同的思路1.互斥同步:包括synchronized和lock等. 2.非阻塞同步:如AtomicInteger的increaseAndGet()方法等. 3 ...

  7. 并发编程之synchronized关键字

    synchronized关键字 synchronized关键字最主要的三种使用方式的总结 1.修饰实例方法,作用于当前对象实例加锁,进入同步代码块前要获得当前对象实例的锁 2.修饰静态方法,作用于当前 ...

  8. 并发编程之wait()、notify()

    前面的并发编程之volatile中我们用程序模拟了一个场景:在main方法中开启两个线程,其中一个线程t1往list里循环添加元素,另一个线程t2监听list中的size,当size等于5时,t2线程 ...

  9. 并发编程之J.U.C的第二篇

    并发编程之J.U.C的第二篇 3.2 StampedLock 4. Semaphore Semaphore原理 5. CountdownLatch 6. CyclicBarrier 7.线程安全集合类 ...

随机推荐

  1. 在VisualStudio中为GUI程序添加console

    1.使用WINDOWS提供的一系列ReadConsole,WriteConsole等API来完成这个功能,具体参见MSDN HANDLE hOut = GetStdHandle(STD_OUTPUT_ ...

  2. 有a1,a2,a3,a4,四个数组,四个数组重新组合成一个数组(A),间隔是10个元素

    好久没折腾Py了,这是去年年初2019.3月发在Q中的一个记录,因不从事这个,并且被在工厂耽误10几年,所以很少写Blog在这里,感觉这里比较正式,而在Q中只是随意性的记载, 但发布图片总是需要另外再 ...

  3. C#数据结构与算法系列(四):链表——单链表(Single-LinkedList)

    1.介绍: 链表是有序的列表,但是它在内存的存储如下:  链表是以节点的方式来存储,链式存储 每一个节点包含data域,next域:指向下一个节点 链表的各个节点不一定是连续存储 链表分带头节点的链表 ...

  4. range用法(倒序取值)

    range(4,-1,-1) #倒数取值 ''' start: 计数从 start 开始.默认是从 0 开始.例如range(5)等价于range(0, 5); stop: 计数到 stop 结束,但 ...

  5. 使用Vim写LaTeX代码(Vim+Vimtex+Skim)

    最近在写博客的时候发现对数学公式的支持并不好,于是就想寻找一个解决方案.我本身是一个爱折腾的人,有时尽管有现成的解决方案我有事也不愿意去用.于是多方查找资料,想寻求一个自定义的解决方案,最终把自己的目 ...

  6. 图像处理中的valid卷积与same卷积

    valid卷积 在full卷积的卷积过程中,会遇到\(K_{flip}\)靠近I的边界(K矩阵与I矩阵),就会有部分延申到I之外,这时候忽略边界,只考虑I完全覆盖\(K_{flip}\)内的值情况,这 ...

  7. python 函数式编程 高阶函数 装饰器

    # -*- coding:gb2312 -*- #coding=utf-8 # 高阶函数 import math def is_sqr(x): y = int(math.sqrt(x)) return ...

  8. 想学好Python,你必须了解Python中的35个关键词

    每种编程语言都会有一些特殊的单词,称为关键词.对待关键词的基本要求是,你在命名的时候要避免与之重复.本文将介绍一下Python中的关键词.关键词不是内置函数或者内置对象类型,虽然在命名的时候同样也最好 ...

  9. idea的maven项目无法引入junit类

    本机:java版本:1.8 pom中是junit版本:4.12 出现问题:在使用@Test 无法引入 : org.junit.Test; 解决方法:junit在pom.xml改为 4.12-beta- ...

  10. JavaWeb网上图书商城完整项目--day02-5.ajax校验功能之服务器端三层实现

    regist.jsp页面中有异步请求服务器来对表单进行校验: l  校验登录名是否已注册过: l  校验Email是否已注册过: l  校验验证码是否正确. 这说明在UserServlet中需要提供相 ...