当前位置: 首页 > news >正文

网站seo优化很好徐州百都网络点赞万家建设有限公司网站

网站seo优化很好徐州百都网络点赞,万家建设有限公司网站,网站开发如何使用API,内涵图网站源码synchronized 作用synchronized 关键字是 Java 并发编程中线程同步的常用手段之一。1.1 作用#xff1a;确保线程互斥的访问同步代#xff0c;锁自动释放#xff0c;多个线程操作同个代码块或函数必须排队获得锁#xff0c;保证共享变量的修改能够及时可见#xff0c;获得…synchronized 作用synchronized 关键字是 Java 并发编程中线程同步的常用手段之一。1.1 作用确保线程互斥的访问同步代锁自动释放多个线程操作同个代码块或函数必须排队获得锁保证共享变量的修改能够及时可见获得锁的线程操作完毕后会将所数据刷新到共享内存区不解决重排序但保证有序性。1.2 用法修饰实例方法synchronized 关键词作用在方法的前面用来锁定方法其实默认锁定的是 this 对象。public class Thread1 implements Runnable{ //共享资源(临界资源) static int i0; //如果没有synchronized关键字输出小于20000 public synchronized void increase(){ i; } public void run() { for(int j0;j10000;j){ increase(); } } public static void main(String[] args) throws InterruptedException { Thread1 tnew Thread1(); Thread t1new Thread(t); Thread t2new Thread(t); t1.start(); t2.start(); t1.join();//主线程等待t1执行完毕 t2.join();//主线程等待t2执行完毕 System.out.println(i); }}修饰静态方法synchronized 还是修饰在方法上不过修饰的是静态方法等价于锁定的是 Class 对象。 public class Thread1 { //共享资源(临界资源) static int i 0; //如果没有synchronized关键字输出小于20000 public static synchronized void increase() { i; } public static void main(String[] args) throws InterruptedException { Thread t1 new Thread(new Runnable() { public void run() { for (int j 0; j 10000; j) { increase(); } } }); Thread t2 new Thread(new Runnable() { Override public void run() { for (int j 0; j 10000; j) { increase(); } } }); t1.start(); t2.start(); t1.join();//主线程等待t1执行完毕 t2.join();//主线程等待t2执行完毕 System.out.println(i); }}修饰代码块用法是在函数体内部对于要修改的参数区间用 synchronized 来修饰相比与锁定函数这个范围更小可以指定锁定什么对象。public class Thread1 implements Runnable { //共享资源(临界资源) static int i 0; Override public void run() { for (int j 0; j 10000; j) { //获得了String的类锁 synchronized (String.class) { i; } } } public static void main(String[] args) throws InterruptedException { Thread1 t new Thread1(); Thread t1 new Thread(t); Thread t2 new Thread(t); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(i); }}总结synchronized 修饰的实例方法多线程并发访问时只能有一个线程进入获得对象内置锁其他线程阻塞等待但在此期间线程仍然可以访问其他方法。synchronized 修饰的静态方法多线程并发访问时只能有一个线程进入获得类锁其他线程阻塞等待但在此期间线程仍然可以访问其他方法。synchronized 修饰的代码块多线程并发访问时只能有一个线程进入根据括号中的对象或者是类获得相应的对象内置锁或者是类锁每个类都有一个类锁类的每个对象也有一个内置锁它们是互不干扰的也就是说一个线程可以同时获得类锁和该类实例化对象的内置锁当线程访问非 synchronzied 修饰的方法时并不需要获得锁因此不会产生阻塞。管程管程(英语Monitors也称为监视器) 在操作系统中是很重要的概念管程其实指的是管理共享变量以及管理共享变量的操作过程。有点扮演中介的意思管程管理一堆对象多个线程同一时候只能有一个线程来访问这些东西。管程可以看做一个软件模块它是将共享的变量和对于这些共享变量的操作封装起来形成一个具有一定接口的功能模块进程可以调用管程来实现进程级别的并发控制。进程只能互斥地使用管程即当一个进程使用管程时另一个进程必须等待。当一个进程使用完管程后它必须释放管程并唤醒等待管程的某一个进程。管程解决互斥问题相对简单需要把共享变量以及共享变量的操作都封装在一个类中。当线程 A 和线程 B 需要获取共享变量 count 时就需要调用 get 和 set 方法而 get 和 set 方法则保证互斥性保证每次只能有一个线程访问。生活中举例管程比如链家店长分配给每个中介管理一部分二手房源多个客户通过中介进行房屋买卖。中介就是管程。多个二手房源被一个中介管理中就是一个管程管理着多个系统资源。多个客户就相当于多个线程。Synchronzied 的底层原理对象头解析我们知道在 Java 的 JVM 内存区域中一个对象在堆区创建创建后的对象由对象头、实例变量、填充数据三部分组成。这三部分功能如下填充数据由于虚拟机要求对象起始地址必须是 8 字节的整数倍。填充数据不是必须存在的仅仅是为了字节对齐。实例变量存放类的属性数据信息包括父类的属性信息这部分内存按 4 字节对齐。对象头主要包括两部分 Klass Point 跟 Mark Word。Klass Point (类型指针)是对象指向它的类元数据的指针虚拟机通过这个指针来确定这个对象是哪个类的实例。Mark Word (标记字段)这一部分用于储存对象自身的运行时的数据如哈希码、GC 分代年龄、锁状态标志、锁指针等。这部分数据在 32 bit 和 64 bit 的虚拟机中大小分别为 32 bit 和 64 bit考虑到虚拟机的空间效率Mark Word 被设计成一个非固定的数据结构以便在极小的空间中存储尽量多的信息它会根据对象的状态复用自己的存储空间(跟 ConcurrentHashMap 里的标志位类似)详细情况如下图synchronized 不论是修饰方法还是代码块都是通过持有修饰对象的锁来实现同步synchronized 锁对象是存在对象头 Mark Word。其中轻量级锁和偏向锁是 Java6 对 synchronized 锁进行优化后新增加的。这里我们主要分析一下重量级锁也就是通常说 synchronized 的对象锁。所标识位为 10其中指针指向的是 monitor 对象(也称为管程或监视器锁)的起始地址。每个对象都存在着一个 monitor 与之关联。反汇编查看分析对象的 monitor 前我们先通过反汇编看下同步方法跟同步方法块在汇编语言级别是什么样的指令。public class SynchronizedTest { public synchronized void doSth(){ System.out.println(Hello World); } public void doSth1(){ synchronized (SynchronizedTest.class){ System.out.println(Hello World); } }}javac SynchronizedTest .java 然后 javap -c SynchronizedTest 反编译后看汇编指令如下 public synchronized void doSth(); descriptor: ()V flags: ACC_PUBLIC, ACC_SYNCHRONIZED // 这是重点 方法锁 Code: stack2, locals1, args_size1 0: getstatic #2 3: ldc #3 5: invokevirtual #4 8: return public void doSth1(); descriptor: ()V flags: ACC_PUBLIC Code: stack2, locals3, args_size1 0: ldc #5 2: dup 3: astore_1 4: monitorenter // 进入同步方法 5: getstatic #2 8: ldc #3 10: invokevirtual #4 13: aload_1 14: monitorexit //正常时 退出同步方法 15: goto 23 18: astore_2 19: aload_1 20: monitorexit // 异常时 退出同步方法 21: aload_2 22: athrow 23: return我们可以看到 Java 编译器为我们生成的字节码。在对于 doSth 和 doSth1 的处理上稍有不同。也就是说JVM 对于同步方法和同步代码块的处理方式不同。对于同步方法JVM 采用ACC_SYNCHRONIZED 标记符来实现同步。对于同步代码块。JVM 采用 monitorenter、monitorexit 两个指令来实现同步。ACC_SYNCHRONIZED方法级的同步是隐式的。同步方法的常量池中会有一个 ACC_SYNCHRONIZED 标志。当某个线程要访问某个方法的时候会检查是否有 ACC_SYNCHRONIZED如果有设置则需要先获得监视器锁然后开始执行方法方法执行之后再释放监视器锁。这时如果其他线程来请求执行方法会因为无法获得监视器锁而被阻断住。值得注意的是如果在方法执行过程中发生了异常并且方法内部并没有处理该异常那么在异常被抛到方法外面之前监视器锁会被自动释放。monitorenter 跟 monitorexit可以把执行 monitorenter 指令理解为加锁执行 monitorexit 理解为释放锁。每个对象维护着一个记录着被锁次数的计数器。未被锁定的对象的该计数器为 0当一个线程获得锁(执行 monitorenter )后该计数器自增变为 1 当同一个线程再次获得该对象的锁的时候计数器再次自增。当同一个线程释放锁(执行 monitorexit 指令)的时候计数器再自检。当计数器为 0 的时候。锁将被释放其他线程便可以获得锁。结论同步方法和同步代码块底层都是通过 monitor 来实现同步的。两者区别同步方式是通过方法中的 access_flags 中设置 ACC_SYNCHRONIZED 标志来实现同步代码块是通过 monitorenter 和 monitorexit 来实现。monitor 解析每个对象都与一个 monitor 相关联而 monitor 可以被线程拥有或释放在Java 虚拟机( HotSpot )中monitor 是由 ObjectMonitor 实现的其主要数据结构如下(位于 HotSpot 虚拟机源码 ObjectMonitor.hpp 文件C实现的)。ObjectMonitor() { _count 0; //记录数 _recursions 0; //锁的重入次数 _owner NULL; //指向持有ObjectMonitor对象的线程 _WaitSet NULL; //调用wait后线程会被加入到_WaitSet _EntryList NULL ; //等待获取锁的线程会被加入到该列表}monitor 运行图如下对于一个 synchronized 修饰的方法(代码块)来说当多个线程同时访问该方法那么这些线程会先被放进_EntryList 队列此时线程处于 blocked 状态当一个线程获取到了对象的 monitor 后那么就可以进入 running 状态执行方法块此时ObjectMonitor 对象的_owner 指向当前线程_count 加 1 表示当前对象锁被一个线程获取当 running 状态的线程调用 wait() 方法那么当前线程释放 monitor 对象进入 waiting 状态ObjectMonitor 对象的_owner 变为 null_count 见 1同时线程进入_WaitSet 队列直到有线程调用 notify() 方法唤醒该线程则该线程进入_EntryList 队列竞争到锁再进入_owner区如果当前线程执行完毕那么也释放 monitor 对象ObjectMonitor 对象的_owner 变为 null_count 见 1。因为监视器锁(monitor)是依赖于底层的操作系统的 Mutex Lock 来实现的而操作系统实现线程之间的切换时需要从用户态转换到核心态(具体可看CXUAN 写的 OS 哦)这个状态之间的转换需要相对比较长的时间时间成本相对较高这也是早期的 synchronized 效率低的原因。庆幸在 Java 6 之后Java 官方对从 JVM 层面对 synchronized 较大优化最终提升显著Java 6 之后为了减少获得锁和释放锁所带来的性能消耗引入了锁升级的概念。锁升级synchronized 锁有四种状态无锁、偏向锁、轻量级锁、重量级锁。这几个状态会随着竞争状态逐渐升级锁可以升级但不能降级但是偏向锁状态可以被重置为无锁状态。科学性的说这些锁之前我们先看个简单通俗的例子来加深印象。通俗说法理解各种锁偏向锁、轻量级锁和重量级锁之间的关系首先打个比方假设现在厕所只有一个位置每个使用者都有打开门锁的钥匙。必须打开门锁才能使用厕所。其中小明、小红理解为两个线程上厕所理解为执行同步代码门锁理解为同步代码的锁小明今天吃坏了东西需要反复去厕所如果小明每次都要开锁就很耽误时间于是门锁将小明的脸记录下来(假设那个锁是智能锁)下次小明再来的时候门锁会自动识别出是小明来了然后自动开锁这样就省去了小明拿钥匙开门的过程此时门锁就是偏向锁也可以理解为偏向小明的锁。接下来小红又去上厕所试图将厕所的门锁设置为偏向自己的偏向锁于是发现门锁无法偏向自己因为此时门锁已是偏向小明的偏向锁。于是小红很生气要求门锁撤销对小明的偏向当然小明也不同意门锁偏向小红。于是等小明用完厕所之后门锁撤销了对任何人的偏向(只要出现竞争的情况就会撤销偏向锁)。这个过程就是撤销偏向锁。此时门锁升级为轻量级锁。等小明出来以后轻量级锁正式生效 。下一次小明和小红同时来厕所谁跑的快谁先走到门前开门后将门锁拿进厕所并将门锁打开以后拿进厕所里将门反锁于是在门外原来放门锁的位置放置了一个有人的标志(这个标识可以理解为指向门锁的指针或者理解为作为锁的 Java 对象头的 Mark Word 值)这时小红看到有人以后很着急催着里面的人出来时马上进去于是不断的来敲门问小明什么时候出来。这个过程就是自旋。反复敲了几次以后小明受不了了对小红喊话说你别敲了等我用完厕所我告诉你于是小红去一边等着(线性阻塞)。此时门锁升级为重量级锁。升级为重量级锁的后果就是小红不再反复敲门小明在上完厕所以后必须告诉小红一声否则小红就会一直等着。结论偏向锁在只有一个人上厕所时非常高效省去了开门的过程。轻量级锁在有多人上厕所但是每个人使用的特别快的时候比较高效因为会出现这种现象小红敲门的时候正好赶上小明出来这样就省得小明出来告诉小红以后小红才能进去但是这样可能会出现小红敲门失败的情况(就是敲门时小明还没用完)。重量级锁相比与轻量级锁的多了一步小明呼唤小红的步骤但是却省掉了小红反复去敲门的过程但是能保证小红去厕所时厕所一定是没人的。偏向锁经过 HotSpot 作者大量的研究发现大多数时候是不存在锁竞争的经常是一个线程多次获得同一个锁因此如果每次都要竞争锁会增大很多没有必要付出的代价为了降低获取锁的代价才引入的偏向锁。核心思想如果一个线程获得了锁那么锁就进入偏向模式此时 Mark Word 的结构也变为偏向锁结构当这个线程再次请求锁时无需再做任何同步操作即获取锁的过程。这样就省去了大量有关锁申请的操作从而也就提供程序的性能。所以对于没有锁竞争的场合偏向锁有很好的优化效果毕竟极有可能连续多次是同一个线程申请相同的锁。但是对于锁竞争比较激烈的场合偏向锁就失效了因为这样场合极有可能每次申请锁的线程都是不相同的。因此这种场合下不应该使用偏向锁否则会得不偿失需要注意的是偏向锁失败后并不会立即膨胀为重量级锁而是先升级为轻量级锁。具体流程当线程 1 访问代码块并获取锁对象时会在 java 对象头和栈帧中记录偏向的锁的 threadID因为偏向锁不会主动释放锁。因此以后线程 1 再次获取锁的时候需要比较当前线程的 threadID 和 Java 对象头中的threadID 是否一致如果一致(还是线程 1 获取锁对象)则无需使用 CAS 来加锁、解锁如果不一致(其他线程如线程 2 要竞争锁对象而偏向锁不会主动释放因此还是存储的线程 1 的 threadID)那么需要查看Java 对象头中记录的线程 1 是否存活如果没有存活那么锁对象被重置为无锁状态其它线程(线程 2)可以竞争将其设置为偏向锁如果存活那么立刻查找该线程(线程 1)的栈帧信息如果还是需要继续持有这个锁对象那么暂停当前线程 1撤销偏向锁升级为 轻量级锁如果线程 1 不再使用该锁对象那么将锁对象状态设为无锁状态重新偏向新的线程。轻量级锁轻量级锁考虑的是竞争锁对象的线程不多而且线程持有锁的时间也不长的情景。因为阻塞线程需要高昂的耗时实现 CPU 从用户态转到内核态的切换如果刚刚阻塞不久这个锁就被释放了那这个代价就有点得不偿失了因此这个时候就干脆不阻塞这个线程让它自旋这等待锁释放。原理跟升级线程 1 获取轻量级锁时会先把锁对象的对象头 MarkWord 复制一份到线程 1 的栈帧中创建的用于存储锁记录的空间(称为DisplacedMarkWord )然后使用 CAS 把对象头中的内容替换为线程 1 存储的锁记录(DisplacedMarkWord)的地址如果在线程 1 复制对象头的同时(在线程 1 CAS 之前)线程 2 也准备获取锁复制了对象头到线程 2 的锁记录空间中但是在线程 2 CAS 的时候发现线程 1 已经把对象头换了「线程 2 的 CAS 失败那么线程 2 就尝试使用自旋锁来等待线程 1 释放锁」。自旋锁简单来说就是让线程 2 在循环中不断 CAS 尝试获得锁对象。但是如果自旋的时间太长也不行因为自旋是要消耗 CPU 的因此自旋的次数是有限制的。比如 10 次或者 100 次如果自旋次数到了线程 1 还没有释放锁或者线程 1 还在执行线程 2 还在自旋等待那么这个时候轻量级锁就会膨胀为重量级锁。重量级锁把除了拥有锁的线程都阻塞防止 CPU 空转。锁消除消除锁是虚拟机另外一种锁的优化这种优化更彻底Java 虚拟机在 JIT 编译时通过对运行上下文的扫描去除不可能存在共享资源竞争的锁通过这种方式消除没有必要的锁可以节省毫无意义的请求锁时间我们知道StringBuffer 是线程安全的里面包含锁的存在但是如果我们在函数内部使用 StringBuffer 那么代码会在 JIT 后会自动将锁释放掉哦。对比如下锁状态优点缺点适用场景偏向锁加锁解锁无需额外消耗跟非同步方法时间相差纳秒级别如果竞争线程多会带来额外的锁撤销的消耗基本没有其他线程竞争的同步场景轻量级锁竞争的线程不会阻塞而是在自旋可提高程序响应速度如果一直无法获得会自旋消耗CPU少量线程竞争持有锁时间不长追求响应速度重量级锁线程竞争不会导致 CPU 自旋跟消耗 CPU 资源线程阻塞响应时间长很多线程竞争锁切锁持有时间长追求吞吐量时候PSReentrantLock 底层实现依赖于特殊的 CPU 指令比如发送 lock 指令和 unlock 指令不需要用户态和内核态的切换所以效率高。而synchronized 底层由监视器锁(monitor)是依赖于底层的操作系统的Mutex Lock 需要用户态和内核态的切换所以效率会低一些。锁升级流程图最后奉上 unbelievableme 绘制的锁升级大图。
http://www.zqtcl.cn/news/612609/

相关文章:

  • 北京做网站电话的公司网站怎么做外链
  • 手工艺品外贸公司网站建设方案复古风格网站
  • 企业网站后端模板如何编写手机程序
  • 泰州网站建设服务好wordpress 子分类
  • 做个企业网站要多少钱php mysql怎么编写视频网站
  • 精仿手表网站做网站为什么要做备案接入
  • 哈什么网一个网站做ppt清新区城乡建设局网站
  • 重庆专业网站建设首页排名网站模板广告去除
  • 河南省建设行业证书查询网站怎么用ps做网站首页背景图片
  • 如何取一个大气的名字的做网站青岛北方现货交易平台
  • 关于做书的网站购物网站建设资讯
  • 运营网站开发工作招聘做装修有什么好网站可以做
  • 免费自学平面设计的网站直播网站开发源码
  • 电子商务网站建设实践广州网站建设公司怎么选
  • 做公众号的素材网站分销电商平台有哪些
  • 网站后期维护协议如何免费注册网址
  • 内容展示型网站 设计特点福州百度seo
  • 外贸网站 推广影视广告宣传片制作公司
  • crm系统管理大兴安岭地网站seo
  • 免费 网站模板为什么自己做的网站别的电脑打不开
  • 公司网站建设建设辽宁鞍山网站建设
  • 企业为什么做网站优化推广做网站学什么什么专业
  • 怎样访问简版网站中小企业网站建设济南兴田德润电话
  • 哪里有零基础网站建设教学服务常用知名购物网站
  • 西宁高端企业网站建设公司名称大全免费取名
  • 如何解决网站图片打开慢关键词搜索推广排行榜
  • 网站建设销售话建网站需要怎样做
  • 网站排名和什么有关网络推广协议合同范本
  • 湖州房产网站建设南通市城乡和住房建设局网站
  • 郴州建设工程集团招聘信息网站wordpress 橘子皮模板