你的位置:首页 > Java教程

[Java教程]Java synchronized关键字用法(清晰易懂)

本篇随笔主要介绍 java 中 synchronized 关键字常用法,主要有以下四个方面:

  1、实例方法同步

  2、静态方法同步

  3、实例方法中同步块

  4、静态方法中同步块

  

  我觉得在学习synchronized关键字之前,我们首先需要知道以下一点:Java 中每个实例对象对应一把锁且每个实例对象只有一把锁,synchronized 关键字是通过对相应的实例对象加锁来实现同步功能的。

  

  一、实例方法中使用 synchronized 加锁

  实例方法中默认被加锁的对象是调用此方法的实例对象。

  

 1 class ImmutableValue { 2   public synchronized void comeIn() throws InterruptedException{ 3     System.out.println(Thread.currentThread().getName() + ": start"); 4     Thread.sleep(5000); 5     System.out.println(Thread.currentThread().getName() + ": finish"); 6   } 7   public void synchronized comeInIn() throws InterruptedException { 8     System.out.println(Thread.currentThread().getName() + ": start"); 9     Thread.sleep(5000);10     System.out.println(Thread.currentThread().getName() + ": finish");11   }12 }13 public class TestImmutableValue {14   public static void main(String[] args) {15     ImmutableValue im = new ImmutableValue();16     Thread t1 = new Thread(new Runnable() {17 18       @Override19       public void run() {20         // TODO Auto-generated method stub21         try {22           im.comeIn();23         } catch (InterruptedException e) {24           // TODO Auto-generated catch block25           e.printStackTrace();26         }27       }28       29     }, "t1");30     Thread t2 = new Thread(new Runnable() {31 32       @Override33       public void run() {34         // TODO Auto-generated method stub35         try {36           im.comeInIn();37         } catch (InterruptedException e) {38           // TODO Auto-generated catch block39           e.printStackTrace();40         }41       }42       43     }, "t2");44     t1.start();45     t2.start();46   }47 }

 

    在上面的代码中创建了两个线程并分别命名为 t1, t2。调用了同一个对象 im 的两个同步方法 comeIn 和 comeInIn, 执行结果如下:

      

    在 t1 线程开始执行后,即使 t1 线程睡眠了5s,线程 t2 中的 comeInIn 方法仍然没有得到执行。这是因为 t1 线程先执行的 comeIn 方法,持有了对象 im 的锁,且 comeIn 方法并没有执行完,对象 im 的锁没有被释放,所以 comeInIn 方法无法对对象 im 加锁,就无法继续执行,只能等到 t1 线程中的 comeIn 方法执行完毕,释放对象 im 的锁,comeInIn 方法才能继续执行。

    但是如果 t1 线程调用的是对象 im 的 comeIn 方法,而 t2 线程调用的是我们声明的另外一个  ImmutableValue 对象 im2 对象的 comeInIn 方法,则这两个方法的执行是互不影响的。因为 t1 线程的 comeIn 方法要获得 im 对象的锁,而 t2 线程要获得的是 im2 对象的锁,两个锁并不是同一个锁(Java中每个实例对象都有且只有一个锁),所以这两个方法执行互不影响。

    

  二、静态方法中使用 synchronized 加锁

  静态方法中默认被加锁的对象是此静态方法所在类的 class 对象。

  

 1 class staticMethodSynchronized { 2   public static synchronized void method1() throws InterruptedException { 3     System.out.println(Thread.currentThread().getName() + ": start"); 4     Thread.sleep(5000); 5     System.out.println(Thread.currentThread().getName() + ": finish"); 6   } 7   public static synchronized void method2() throws InterruptedException { 8     System.out.println(Thread.currentThread().getName() + ": start"); 9     Thread.sleep(5000);10     System.out.println(Thread.currentThread().getName() + ": finish");11   }12 }13 public class TestStaticClassSynchronized {14   public static void main(String[] args) {15     Thread t1 = new Thread(new Runnable() {16 17       @Override18       public void run() {19         // TODO Auto-generated method stub20         try {21           staticMethodSynchronized.method1();22         } catch (InterruptedException e) {23           // TODO Auto-generated catch block24           e.printStackTrace();25         }26       }27       28     }, "t1");29     Thread t2 = new Thread(new Runnable() {30 31       @Override32       public void run() {33         // TODO Auto-generated method stub34         try {35           staticMethodSynchronized.method2();36         } catch (InterruptedException e) {37           // TODO Auto-generated catch block38           e.printStackTrace();39         }40       }41       42     }, "t2");43     t1.start();44     t2.start();45   }46 }

 

    在上述代码中创建了两个线程并命名为 t1,t2。 t1,t2 线程调用了 staticMethodSynchronized 类的两个静态同步方法 method1 和 method2。执行结果如下:

    

    在 t1 线程开始执行后,即使 t1 线程睡眠了5s,线程 t2 中的 method2 方法仍然没有得到执行。这是因为 t1 线程先执行的 method1 方法,持有了staticMethodSynchronized 类对象的锁,且 method1 方法并没有执行完,staticMethodSynchronized 类对象的锁没有被释放,所以 comeInIn 方法无法对staticMethodSynchronized 类对象加锁,就无法继续执行,只能等到 t1 线程中的 method1 方法执行完毕,释放 staticMethodSynchronized 类对象的锁,method2 方法才能继续执行。

 

  三、实例方法中使用 synchronized 关键字制造同步块

  同步块中默认被加锁的对象是此同步块括号声明中包含的对象。

  

 1 class ImmutableValue { 2   public synchronized void comeIn() throws InterruptedException{ 3     System.out.println(Thread.currentThread().getName() + ": start"); 4     Thread.sleep(5000); 5     System.out.println(Thread.currentThread().getName() + ": finish"); 6   } 7   public void comeInIn() throws InterruptedException { 8     System.out.println(Thread.currentThread().getName() + ": start"); 9     synchronized(this) {10       11     }12     System.out.println(Thread.currentThread().getName() + ": finish");13   }14 }15 public class TestImmutableValue {16   public static void main(String[] args) {17     ImmutableValue im = new ImmutableValue();18     Thread t1 = new Thread(new Runnable() {19 20       @Override21       public void run() {22         // TODO Auto-generated method stub23         try {24           im.comeIn();25         } catch (InterruptedException e) {26           // TODO Auto-generated catch block27           e.printStackTrace();28         }29       }30       31     }, "t1");32     Thread t2 = new Thread(new Runnable() {33 34       @Override35       public void run() {36         // TODO Auto-generated method stub37         try {38           im.comeInIn();39         } catch (InterruptedException e) {40           // TODO Auto-generated catch block41           e.printStackTrace();42         }43       }44       45     }, "t2");46     t1.start();47     t2.start();48   }49 }

 

    由以上代码可以看到: 在 comeInIn 方法中,运用  synchronized(this) 制造同步块,要执行同步块内的代码,就必须获得 this 对象的锁(调用 comeInIn 方法的对象)。

    执行结果可能为:

    

    由此执行结果可见:t1 线程先执行了 comeIn 方法,获得了对象 im 的锁,之后由于 t1 线程进入睡眠状态,t2 线程得到运行,开始执行 comeInIn 方法,当执行到同步代码块时发现对象 im 已被加锁,无法继续执行。t1 线程睡眠结束之后继续执行,结束后释放对象 im 的锁,t2 线程才能继续执行。

 

  四、静态方法中使用 synchronized 关键字制造同步块

  同步块中默认被加锁的对象是此同步块括号声明中包含的对象。

  

 1 class staticMethodSynchronized { 2   private static final Object OBJ = new Object(); 3   public static void method1() throws InterruptedException { 4     System.out.println(Thread.currentThread().getName() + ": start"); 5     synchronized(OBJ) { 6       System.out.println(Thread.currentThread().getName() + ": 获得锁"); 7       System.out.println(Thread.currentThread().getName() + ": 释放锁"); 8     } 9     System.out.println(Thread.currentThread().getName() + ": finish");10   }11   public static void method2() throws InterruptedException {12     System.out.println(Thread.currentThread().getName() + ": start");13     synchronized(OBJ) {14       System.out.println(Thread.currentThread().getName() + ": 获得锁");15       System.out.println(Thread.currentThread().getName() + ": 释放锁");16     }17     System.out.println(Thread.currentThread().getName() + ": finish");18   }19 }20 public class TestStaticClassSynchronized {21   public static void main(String[] args) {22     Thread t1 = new Thread(new Runnable() {23 24       @Override25       public void run() {26         // TODO Auto-generated method stub27         try {28           staticMethodSynchronized.method1();29         } catch (InterruptedException e) {30           // TODO Auto-generated catch block31           e.printStackTrace();32         }33       }34       35     }, "t1");36     Thread t2 = new Thread(new Runnable() {37 38       @Override39       public void run() {40         // TODO Auto-generated method stub41         try {42           staticMethodSynchronized.method2();43         } catch (InterruptedException e) {44           // TODO Auto-generated catch block45           e.printStackTrace();46         }47       }48       49     }, "t2");50     t1.start();51     t2.start();52   }53 }

 

    在上述代码中,两个静态方法中的同步块都要获得对象 OBJ 的锁才能继续向下执行,执行结果可能如下:

       

    若 t1 线程先获得锁,则必须等到 t1 释放锁之后,t2 线程中同步代码块及其之后的代码才能继续执行,t2 线程先获得锁,t1 线程同理。

 

   总之,我认为我们只需抓住一点:Java 中每个实例对象对应一把锁且每个实例对象只有一把锁,synchronized 关键字是通过对相应的实例对象加锁来实现同步功能的(静态方法为对相应的 class 对象加锁)。在执行 synchronized 方法或 synchronized 同步块之前,我们只需判断其需要获得的对象的锁是否可获得,就可判断此方法或同步块是否可得到执行。