zoukankan      html  css  js  c++  java
  • 阅读笔记八一——java中的锁原理锁优化

    一、为什么要用锁?

    -是为了解决并发操作引起的脏读、数据不一致的问题。

    二、锁实现的基本原理

    2.1volatile

    Java编程语言允许线程访问共享变量, 为了确保共享变量能被准确和一致地更新,线程应该确保通过排他锁单独获得这个变量。Java语言提供了volatile,在某些情况下比锁要更加方便。

    volatile在多处理器开发中保证了共享变量的“ 可见性”。可见性的意思是当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值。

    结论:如果volatile变量修饰符使用恰当的话,它比synchronized的使用和执行成本更低,因为它不会引起线程上下文的切换和调度。

    2.2synchronized

    synchronized通过锁机制实现同步。

    先来看下利用synchronized实现同步的基础:Java中的每一个对象都可以作为锁。

    具体表现为以下3种形式。

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

    对于静态同步方法,锁是当前类的Class对象。

    对于同步方法块,锁是Synchonized括号里配置的对象。

    当一个线程试图访问同步代码块时,它首先必须得到锁,退出或抛出异常时必须释放锁。

    2.2.1 synchronized实现原理

    synchronized是基于Monitor来实现同步的。

    Monitor从两个方面来支持线程之间的同步:

    互斥执行

    协作

    1Java 使用对象锁 ( 使用 synchronized 获得对象锁 ) 保证工作在共享的数据集上的线程互斥执行。

    2、使用 notify/notifyAll/wait 方法来协同不同线程之间的工作。

    3ClassObject都关联了一个Monitor

    Monitor 的工作机理

    线程进入同步方法中。

    为了继续执行临界区代码,线程必须获取 Monitor 锁。如果获取锁成功,将成为该监视者对象的拥有者。任一时刻内,监视者对象只属于一个活动线程(The Owner

    拥有监视者对象的线程可以调用 wait() 进入等待集合(Wait Set),同时释放监视锁,进入等待状态。

    其他线程调用 notify() / notifyAll() 接口唤醒等待集合中的线程,这些等待的线程需要重新获取监视锁后才能执行 wait() 之后的代码。

    同步方法执行完毕了,线程退出临界区,并释放监视锁。

    参考文档:https://www.ibm.com/developerworks/cn/java/j-lo-synchronized

    2.2.2 synchronized具体实现

    1、同步代码块采用monitorentermonitorexit指令显式的实现。

    2、同步方法则使用ACC_SYNCHRONIZED标记符隐式的实现。

    通过实例来看看具体实现:

    public class SynchronizedTest {

      public synchronized void method1(){

           System.out.println("Hello World!");

     }

      public void method2(){

               synchronized (this){

                  System.out.println("Hello World!");

              }

       }

    }

    2.2.3 synchronized的锁优化

    JavaSE1.6为了减少获得锁和释放锁带来的性能消耗,引入了“偏向锁”和“轻量级锁”。

    JavaSE1.6中,锁一共有4种状态,级别从低到高依次是:无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态,这几个状态会随着竞争情况逐渐升级。

    锁可以升级但不能降级,意味着偏向锁升级成轻量级锁后不能降级成偏向锁。这种锁升级却不能降级的策略,目的是为了提高获得锁和释放锁的效率。

    偏向锁:

    无锁竞争的情况下为了减少锁竞争的资源开销,引入偏向锁。

    轻量级锁:

    轻量级锁所适应的场景是线程交替执行同步块的情况。

    锁粗化(Lock Coarsening):也就是减少不必要的紧连在一起的unlocklock操作,将多个连续的锁扩展成一个范围更大的锁。

    锁消除(Lock Elimination):锁削除是指虚拟机即时编译器在运行时,对一些代码上要求同步,但是被检测到不可能存在共享数据竞争的锁进行削除。

    适应性自旋(Adaptive Spinning):自适应意味着自旋的时间不再固定了,而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。如果在同一个锁对象上,自旋等待刚刚成功获得过锁,并且持有锁的线程正在运行中,那么虚拟机就会认为这次自旋也很有可能再次成功,进而它将允许自旋等待持续相对更长的时间,比如100个循环。另一方面,如果对于某个锁,自旋很少成功获得过,那在以后要获取这个锁时将可能省略掉自旋过程,以避免浪费处理器资源。

  • 相关阅读:
    vivado 连接不上板子 There is no current hw_target
    excel 方框打钩
    2019新个税如何计算
    printf("loops %u / %u%c[K ", loops + 1, opts->loops, 27); printf("%cM", 27);
    HttpUtil
    Spring注入bean和aop的注意事项
    获取所有bean的名字
    不可见类有抽象父类,spring配置子类bean,注入父类,aop就可以切父类的方法
    实例/静态工厂方法得到bean
    注入抽象类的子类
  • 原文地址:https://www.cnblogs.com/cc-9878/p/11053303.html
Copyright © 2011-2022 走看看