美文网首页
实现同步的方法

实现同步的方法

作者: 小锋_c3c2 | 来源:发表于2019-11-28 23:54 被阅读0次

    1.互斥同步

    互斥同步(Mutual Exclusion&Synchronization)是常见的一种并发正确性保障手段。同步是指在多个线程并发访问共享数据时,

    保证共享数据在同一个时刻只被一个(或者是一些,使用信号量的时候)线程使用。而互斥是实现同步的一种手段,

    临界区(Critical Section)、互斥量(Mutex)和信号量(Semaphore)都是主要的互斥实现方式。

    因此,在这4个字里面,互斥是因,同步是果;互斥是方法,同步是目的。

    在Java中,最基本的互斥同步手段就是synchronized关键字,synchronized关键字经过编译之后,

    会在同步块的前后分别形成monitorenter和monitorexit这两个字节码指令,

    这两个字节码都需要一个reference类型的参数来指明要锁定和解锁的对象。

    如果Java程序中的synchronized明确指定了对象参数,那就是这个对象的reference;

    如果没有明确指定,那就根据synchronized修饰的是实例方法还是类方法,

    去取对应的对象实例或Class对象来作为锁对象。

    根据虚拟机规范的要求,在执行monitorenter指令时,首先要尝试获取对象的锁。如果这个对象没被锁定,或者当前线程已经拥有了那个对象的锁,

    把锁的计数器加1,相应的,在执行monitorexit指令时会将锁计数器减1,当计数器为0时,锁就被释放。

    如果获取对象锁失败,那当前线程就要阻塞等待,直到对象锁被另外一个线程释放为止。

    在虚拟机规范对monitorenter和monitorexit的行为描述中,有两点是需要特别注意的。

    首先,synchronized同步块对同一条线程来说是可重入的,不会出现自己把自己锁死的问题。

    其次,同步块在已进入的线程执行完之前,会阻塞后面其他线程的进入。J

    ava的线程是映射到操作系统的原生线程之上的,如果要阻塞或唤醒一个线程,都需要操作系统来帮忙完成,这就需要从用户态转换到核心态中

    因此状态转换需要耗费很多的处理器时间。对于代码简单的同步块(如被synchronized修饰的getter()或setter()方法),

    状态转换消耗的时间有可能比用户代码执行的时间还要长。所以synchronized是Java语言中一个重量级(Heavyweight)的操作,

    有经验的程序员都会在确实必要的情况下才使用这种操作。而虚拟机本身也会进行一些优化,譬如在通知操作系统阻塞线程之前加入一段自旋等待过程,

    避免频繁地切入到核心态之中。

    除了synchronized之外,我们还可以使用java.util.concurrent(下文称J.U.C)包中的重入锁(ReentrantLock)来实现同步,

    在基本用法上,ReentrantLock与synchronized很相似,他们都具备一样的线程重入特性,只是代码写法上有点区别,

    一个表现为API层面的互斥锁(lock()和unlock()方法配合try/finally语句块来完成),

    另一个表现为原生语法层面的互斥锁。

    不过,相比synchronized,ReentrantLock增加了一些高级功能,主要有以下3项:等待可中断、可实现公平锁,以及锁可以绑定多个条件。

    synchronized中的锁是非公平的,ReentrantLock默认情况下也是非公平的,但可以通过带布尔值的构造函数要求使用公平锁。

    锁绑定多个条件是指一个ReentrantLock对象可以同时绑定多个Condition对象,而在synchronized中,

    锁对象的wait()和notify()或notifyAll()方法可以实现一个隐含的条件,如果要和多于一个的条件关联的时候,就不得不额外地添加一个锁,

    而ReentrantLock则无须这样做,只需要多次调用newCondition()方法即可。

    2.非阻塞同步

    Non-Blocking Synchronization

    在JDK 1.5之后,Java程序中才可以使用CAS操作,该操作由sun.misc.Unsafe类里面的compareAndSwapInt()和compareAndSwapLong()

    等几个方法包装提供,虚拟机在内部对这些方法做了特殊处理,即时编译出来的结果就是一条平台相关的处理器CAS指令,没有方法调用的过程,

    或者可以认为是无条件内联进去了。

    3.无同步方案

    要保证线程安全,并不是一定就要进行同步,两者没有因果关系。同步只是保证共享数据争用时的正确性的手段,如果一个方法本来就不涉及共享数据,

    那它自然就无须任何同步措施去保证正确性,因此会有一些代码天生就是线程安全的,下面简单地介绍其中的两类。

    可重入代码(Reentrant Code)

    可重入代码有一些共同的特征,例如不依赖存储在堆上的数据和公用的系统资源、用到的状态量都由参数中传入、不调用非可重入的方法等。

    我们可以通过一个简单的原则来判断代码是否具备可重入性:如果一个方法,它的返回结果是可以预测的,只要输入了相同的数据,就都能返回相同的结果,

    那它就满足可重入性的要求,当然也就是线程安全的。

    相关文章

      网友评论

          本文标题:实现同步的方法

          本文链接:https://www.haomeiwen.com/subject/itxwwctx.html