Java 中的每个对象都有一个与之关联的内部锁(Intrinsic lock). 这种锁也称为监视器(Monitor), 这种内部锁是一种排他锁,可以保障原子性,可见性与有序性.。

内部锁是通过 synchronized 关键字实现的.synchronized 关键字修饰代码块,修饰该方法。

修饰代码块的语法:
synchronized( 对象锁 ) {
同步代码块,可以在同步代码块中访问共享数据
}

修饰实例方法就称为同步实例方法。

修饰静态方法称称为同步静态方法。

例示代码如下:

A.同步代码块

1.使用this作为锁对象:

package com.company.intrinsiclock;

/*
* synchronized同步代码块
* this锁对象
* */
public class Test01 {

  public static void main(String[] args) {
    //创建两个线程,分别调用mm()方法
    //先创建Test01对象,通过对象名调用mm()方法
    Test01 obj=new Test01();

    //创建两个线程并启动
    new Thread(new Runnable() {
      @Override
      public void run() {
        obj.mm();//使用的锁对象this就是obj对象
      }
    }).start();

    new Thread(new Runnable() {
      @Override
      public void run() {
        obj.mm();//使用的锁对象this就是obj对象
      }
    }).start();
  }

  //定义方法,设置同步代码块
  public void mm(){
    synchronized (this) {//通常使用this当前对象作为锁对象
      for (int i = 1; i <= 100; i++) {
        System.out.println(Thread.currentThread().getName() + "-->" + i);
      }
    }
  }

}

2.线程的锁不同,不能实现同步,想要同步必须使用同一个锁对象

package com.company.intrinsiclock;

/*
* synchronized同步代码块
* this锁对象,如果线程的锁不同,不能实现同步,想要同步必须使用同一个锁对象
* */
public class Test02 {

  public static void main(String[] args) {

    Test02 obj=new Test02();
    Test02 obj2=new Test02();

    //创建两个线程并启动
    new Thread(new Runnable() {
      @Override
      public void run() {
        obj.mm();//使用的锁对象this是obj对象
      }
    }).start();

    new Thread(new Runnable() {
      @Override
      public void run() {
        obj2.mm();//使用的锁对象this是obj2对象,与第一个线程的锁对象不同,不能实现同步
      }
    }).start();
  }

  //定义方法,设置同步代码块
  public void mm(){
    synchronized (this) {//通常使用this当前对象作为锁对象
      for (int i = 1; i <= 100; i++) {
        System.out.println(Thread.currentThread().getName() + "-->" + i);
      }
    }
  }

}

3.使用常量对象作为锁对象

package com.company.intrinsiclock;

/*
* synchronized同步代码块
* 使用常量对象作为锁对象
* */
public class Test03 {

  public static void main(String[] args) {

    Test03 obj=new Test03();
    Test03 obj2=new Test03();

    //创建两个线程并启动
    new Thread(new Runnable() {
      @Override
      public void run() {
        obj.mm();//使用的锁对象使用的锁对象是OBJ常量
      }
    }).start();

    new Thread(new Runnable() {
      @Override
      public void run() {
        obj2.mm();//使用的锁对象使用的锁对象是OBJ常量
      }
    }).start();
  }

  //定义一个常量作为锁对象
  public static final Object OBJ=new Object();

  //定义方法,设置同步代码块
  public void mm(){
    synchronized (OBJ) {  //也可以使用常量对象作为锁对象,此时即使两个线程的对象不同也可以实现同步
      for (int i = 1; i <= 100; i++) {
        System.out.println(Thread.currentThread().getName() + "-->" + i);
      }
    }
  }

}

4.使用常量对象作为锁对象时,同步代码块代码与其所在的位置无关,因为是同一个锁对象

package com.company.intrinsiclock;

/*
* synchronized同步代码块
* 使用常量对象作为锁对象,方法体代码与其所在的位置无关,只要是同一个锁对象
* */
public class Test04 {

  public static void main(String[] args) {

    Test04 obj=new Test04();
    Test04 obj2=new Test04();

    //创建两个线程并启动
    new Thread(new Runnable() {
      @Override
      public void run() {
        obj.mm();//使用的锁对象this是obj对象
      }
    }).start();

    new Thread(new Runnable() {
      @Override
      public void run() {
        obj2.mm();//使用的锁对象OBJ常量
      }
    }).start();

    //第三个线程调用的是静态方法
    new Thread(new Runnable() {
      @Override
      public void run() {
        sm();//使用的锁对象OBJ常量
      }
    }).start();
  }

  //定义一个常量作为锁对象,方法体代码与其所在的位置(实例方法或者静态方法)无关,只要是同一个锁对象
  public static final Object OBJ=new Object();

  //定义方法,设置同步代码块
  public void mm(){
    synchronized (OBJ) {  //使用的锁对象OBJ常量
      for (int i = 1; i <= 100; i++) {
        System.out.println(Thread.currentThread().getName() + "-->" + i);
      }
    }
  }

  //定义静态方法,设置同步代码块
  public static void sm(){
    synchronized (OBJ) {  //也可以使用常量对象作为锁对象,此时即使两个线程的对象不同也可以实现同步
      for (int i = 1; i <= 100; i++) {
        System.out.println(Thread.currentThread().getName() + "-->" + i);
      }
    }
  }

}

B.同步方法
1.同步实例方法

package com.company.intrinsiclock;

/*
* synchronized同步实例方法
*只要是同一个锁对象就可以实现同步
* */
public class Test0 {

  public static void main(String[] args) {
    //创建两个线程,分别调用mm()方法,mm22()方法
    //先创建Test01对象,通过对象名调用mm()方法
    Test0 obj=new Test0();

    //创建两个线程并启动
    new Thread(new Runnable() {
      @Override
      public void run() {
        obj.mm();//使用的锁对象this就是obj对象
      }
    }).start();

    new Thread(new Runnable() {
      @Override
      public void run() {
        obj.mm22();//使用的锁对象this就是obj对象
      }
    }).start();

    new Thread(new Runnable() {
      @Override
      public void run() {
        new Test0().mm22();//使用的锁对象this不是obj对象,不可以实现同步
      }
    }).start();
  }

  //定义方法,设置同步代码块
  public void mm(){
    synchronized (this) {//通常使用this当前对象作为锁对象
      for (int i = 1; i <= 100; i++) {
        System.out.println(Thread.currentThread().getName() + "-->" + i);
      }
    }
  }

  //使用synchronized修饰设置同步实例方法,默认this作为锁对象
  public synchronized void mm22(){
      for (int i = 1; i <= 100; i++) {
        System.out.println(Thread.currentThread().getName() + "-->" + i);
      }

  }

}

2.同步静态方法

package com.company.intrinsiclock;

/*
* synchronized同步静态方法
*只要是同一个锁对象就可以实现同步,默认的锁对象是当前类的运行时类对象,如Test06.class,有人称他为类锁
* */
public class Test06 {

  public static void main(String[] args) {
    //创建两个线程,分别调用mm()方法,mm22()方法
    //先创建Test01对象,通过对象名调用mm()方法
    Test06 obj=new Test06();

    //创建两个线程并启动
    new Thread(new Runnable() {
      @Override
      public void run() {
        obj.mm();//使用的锁对象是Test06.class
      }
    }).start();

    new Thread(new Runnable() {
      @Override
      public void run() {
        obj.sm22();//使用的锁对象也是Test06.class
      }
    }).start();


  }

  //定义方法,设置同步代码块
  public void mm(){
    //使用当前类的运行时类对象作为锁对象,可以简单的理解为把Test06的字节码文件作为锁对象
    synchronized (Test06.class) {
      for (int i = 1; i <= 100; i++) {
        System.out.println(Thread.currentThread().getName() + "-->" + i);
      }
    }
  }

  //使用synchronized修饰设置同步静态方法,默认当前运行时类Test06.class作为锁对象
  public synchronized  static void sm22(){
      for (int i = 1; i <= 100; i++) {
        System.out.println(Thread.currentThread().getName() + "-->" + i);
      }

  }

}