1. 场景

      在并发编程中存在线程安全问题,主要原因有:存在共享数据,多线程共同操作共享数据。关键字synchronized可以保证在同一时刻,只有一个线程可以执行某个方法或某个代码块,同时synchronized可以保证一个线程的可见性

2. 概念

利用锁的机制来实现同步的(解决数据点不一致性 JMM)

  • 锁机制有如下两种特性
  1. 互斥性
    在同一时间只允许一个线程持有某个对象锁,通过这种特性来实现多线程中的协调机制,这样在同一时间只有一个线程对所需同步的代码块(复合操作)进行访问。互斥性也称为操作的原子性
  2. 可见性
    必须确保在锁被释放之前,对共享变量所做的修改,对于随后获得该锁的另一个线程是可见的(即在获得锁时应获得最新共享变量的值),否则另一个线程可能是在本地缓存的某个副本上继续操作从而引起不一致。

3. synchronized的用法

  • 同步方法(分普通方法和静态方法)
    用法:只需要在方法上面添加synchronized关键字即可。
    同步普通方法:只能作用在单例上面,如果不是单例,同步方法锁将失效。
    同步静态方法:不管你有多少个类实例,同时只有一个线程能获取锁进入这个方法。
  • 同步对象实例
 private static int m = 0;
 private Object obj = new Object();

 public void test1() {
     try {
         synchronized (obj) {
             TimeUnit.MINUTES.sleep(2);
             m++;
         }
     } catch (InterruptedException e) {
         e.printStackTrace();
     }
 }
  • 同步类

锁效果与同步静态方法一样,都是类级别的锁,同时只有一个线程能访问带有同步类锁的方法。

public class synchronizedDemo {
    private static int m = 0;
    public void test2() {
        try {
            synchronized (synchronizedDemo.class) {
                TimeUnit.MINUTES.sleep(2);
                m++;
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
  • 同步this实例

与同步块的用法一致,表示锁住整个当前对象实例,只有获取到这个实例的锁才能进入这个方法。

private static int m = 0;
    public void test2() {
        try {
            synchronized (this) {
                TimeUnit.MINUTES.sleep(2);
                m++;
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

4. synchronized底层语义原理

可通过工具jconsole和jstack去观察synchronized的底层以及线程情况。

  • 方法锁:

方法锁就是由关键字ACC_SYNCHRONIZED实现是否互斥方法,

  • 对象锁:

monitor:在jvm规范中每个对象和类在逻辑上都是和一个监视器(monitor)相关联的,为了实现监视器的排他性监视能力,JVM为每一个对象和类都关联一个锁,锁住了一个对象,这就是获得对象相关联的监视器。
实现原理:某一个线程占有这个对象的时候,首先monitor的计数器是不是0,如果是0表示还没有线程占有这个时候线程占有这个对象,并且对这个对象的monitor+1;如果不为0表示这个对象已经被其他线程占有,这个线程等待。当线程释放占有权的时候monitor-1。
注:同一个线程可以对同一个对象多次加锁,+1,+1,重入锁


雄帅
237 声望6 粉丝

我是一个完美主义开发者^_^