开头:

互斥锁是为了保证同一个方法同时间只有一个线程去执行,这个也是在多线程开发当中最基本的实现。在java体系当中有很多方法可以实现目的,如: synchronized ,lock ,redis分布式锁,zk分布式锁,基于数据库实现悲观锁等等。 本文将介绍synchronized这个java原生支持的互斥锁。

简易的锁模型

redission 互斥锁实现 synchronized互斥锁_互斥锁


我们把一段需要互斥的代码称为:临界区。 当线程需要进入临界区的时候,线程需要去尝试获取锁,当获取成功之后就进去临界区执行代码,执行结束之后就去释放锁。 如果在获取线程的时候,其他线程正在使用锁,就会阻塞等待。

使用方法

class X {
  // 修饰非静态方法
  synchronized void foo() {
    // 临界区
  }
  // 修饰静态方法
  synchronized static void bar() {
    // 临界区
  }
  // 修饰代码块
  Object obj = new Object();
  void baz() {
    synchronized(obj) {
      // 临界区
    }
  }
}

这段代码示例中没有出现加锁和解锁的方法,他们的实现是由java编译器在synchronized修饰的方法或者代码块前后自动加上的。
synchronized使用时候有以下几条规则:

  1. 当修饰静态方法的时候,锁定的是当前类的Class对象,在上面的例子 就是Class X.
  2. 当修饰非静态方法的时侯,锁定的是当前实现对象this。

示例: 用synchronized解决 count+=1问题

MyCalc这个类有两个方法:一个是get()方法,用来获取value的值;另一个是addOne()方法,用来给value加1,并且addOne()方法我们用Synchronized修饰。

class SafeCalc {
  long value = 0L;
  long get() {
    return value;
  }
  synchronized void addOne() {
    value += 1;
  }
}

因为addOne()方法被synchronized修饰之后,这个方法就会同时只有一个线程去执行,所以一定能保证原子操作。因为我们的value方法是成员变化,会不会有线程可见性问题呢?
这里就会出来另一个概念: 管程与管程中锁的规则

1. 管程: 就是我们这里的synchronized。
2. 管程中锁的规则: 对一个锁的解锁 Happens-before 做用于后续这个锁的加锁。
第二句话的意思是指 前一个线程的解锁操作对后一个线程的加锁操作是可见的,综合Happens-before的传递性规则,我们就得出前一个线程在临界区修改的共享变更(该操作在解锁之前),对后续进入临界区(该操作在加锁之后)的线程是可见的。

按照这个规则,如果多个线程同时操作addOne()这个方法, 可见性是可以保证的,也就是说如果有100 个线程去执行addOne()这个方法,最终的value的结果会是增加了100。

但是此时还是有一个问题,get()我方法的可见性是无法保证的。管程中锁的规则,是只保证后续对这个锁的加锁的可见性,而get()方法并没有加锁操作。所以可见性是无法保证的。我们可以通过给get() 方法加上synchronized修饰来保证可见性。

class SafeCalc {
  long value = 0L;
  synchronized long get() {
    return value;
  }
  synchronized void addOne() {
    value += 1;
  }
}

更改后的模型:

redission 互斥锁实现 synchronized互斥锁_可见性_02

错误方式

package com.bdf.blog.thread.Synchronized;

/**
 * @program: blog
 * @description:
 * @author: canghaihongxin
 * @create: 2019-04-21 12:24
 **/
public class SafeCacl {

    static int value ;

    public synchronized int getValue() {
        return value;
    }

    public synchronized static void addOne(){
        value +=1;
    }

}

这样就是一个错误的代码示例, 当我们在addOne()方法上添加了static 关键字的时候就Synchronized锁的就是我SafeCacl.class这个类,也就是类锁。synchronized锁的是this。

这个时候getValue()方法就无法保证可见性了。

redission 互斥锁实现 synchronized互斥锁_加锁_03