synchronized基本概念

269 阅读4分钟

关于synchronized的基本介绍

    为了解决线程安全的问题,java提供了synchronized关键字来保证执行临界区代码的原子性,比如synchronized可以有以下两种使用的方式:
1.synchronized 同步方法:使用synchronized修饰一个方法的时候,这个方法被声明为同步方法

public synchronized void selfPlus(){
         amount++;
}

   同步方法的时候,synchronized的位置处于方法的返回类型之前,在这个方法中设置synchronized关键字,那么它的方法的执行流程就是排他性的,可以理解为是一种排它锁,任何时间还在允许有一个线程进入同步方法,也就是临界区代码块,那么如果其他的线程想要进入临界区代码的话就只能进行排队等待了。

2.synchronized同步代码块

synchronized(syncObject){ //同步块而不是方法
//临界区代码段的代码块
}

    这两种方式有什么区别呢?我们可以得出以下结论:对于小的临界区,我们直接在方法声明中设置synchronized同步关键字,可以避免竞态条件的问题,但是是对于较大的临界区代码段,为了执行效率,最好将同步方法分为小的临界区代码段,比如以下例子:

public class TwoPlus {
    //两个临界区资源
    private int sum1 = 0;
    private int sum2 = 0;
    //同步方法
    public synchronized void plus(int val1, int val2){
    //临界区代码段(但是只操作了一个临界区资源,这时候别的线程还操作不了另一个资源)
      this.sum1 += val1;
   }
}

在以上代码中临界区代码段包含对两个临界区资源的操作,一个线程操作两个临界区的资源(但是这个时候只操作了sum1,没有操作sum2),但是别的线程这时候想操作sum2的时候会阻塞,不能操作,这样方法的吞吐量会大大降低,所以我们可以用同步代码块解决这个问题:

public class TwoPlus{
    private int sum1 = 0;
    private int sum2 = 0;
    private Integer sum1Lock = new Integer(1); // 同步锁一
    private Integer sum2Lock = new Integer(2); // 同步锁二
    public void plus(int val1, int val2){
    //同步块1
    synchronized(this.sum1Lock){
     this.sum1 += val1;
 }
   //同步块2
   synchronized(this.sum2Lock){
     this.sum2 += val2;
    }
  }
 }

    两个独立的临界区资源sum1和sum2的加法操作可以并发执行了,在某一个时刻,不同的线程可以对sum1和sum2同时进行加法操作,提升了plus()方法的吞吐量,同步块1和同步块2保护着两个独立的临界区代码段,需要两把不同的syncObject对象锁,从而解决了资源限制的问题

二者的区别
1.synchronized方法是一种粗粒度的并发控制,某一时刻只能有一个线程执行该synchronized方法
2.synchronized代码块是一种细粒度的并发控制,处于synchronized块之外的其他代码是可以被多个线程并发访问的

二者联系
1.在Java的内部实现上,synchronized方法实际上等同于用一个synchronized代码块,这个代码块包含同步方法中的所有语句
2.然后在synchronized代码块的括号中传入this关键字,使用this对象锁作为进入临界区的同步锁

3.静态的同步方法
synchronized被加载静态方法上:

public class SafeStaticMethodPlus{ 
  //静态的临界区资源
  private static Integer amount = 0;
  //使用synchronized关键字修饰 static方法
 public static synchronized void selfPlus(){
    amount++;
  }
}

synchronized锁的是什么?

当synchronized放在方法上时:锁的是当前的实例this
当synchronized放在代码块上的时候:锁的是对象,这个对象可以为this,也可以为其他对象
当synchronized放在静态方法上的时候:锁的是类所对应的Class对象的对象锁(类锁)

    但是由于类的对象实例可以有很多,但是每个类只有一个Class实例,因此使用类锁作为synchronized的同步锁时会造成同一个JVM内的所有线程只能互斥地进入临界区段,所以synchronized关键字修饰static方法是非常粗粒度的同步机制

内容基于java高并发核心编程