• 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             @Override
    19             public void run() {
    20                 // TODO Auto-generated method stub
    21                 try {
    22                     im.comeIn();
    23                 } catch (InterruptedException e) {
    24                     // TODO Auto-generated catch block
    25                     e.printStackTrace();
    26                 }
    27             }
    28             
    29         }, "t1");
    30         Thread t2 = new Thread(new Runnable() {
    31 
    32             @Override
    33             public void run() {
    34                 // TODO Auto-generated method stub
    35                 try {
    36                     im.comeInIn();
    37                 } catch (InterruptedException e) {
    38                     // TODO Auto-generated catch block
    39                     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             @Override
    18             public void run() {
    19                 // TODO Auto-generated method stub
    20                 try {
    21                     staticMethodSynchronized.method1();
    22                 } catch (InterruptedException e) {
    23                     // TODO Auto-generated catch block
    24                     e.printStackTrace();
    25                 }
    26             }
    27             
    28         }, "t1");
    29         Thread t2 = new Thread(new Runnable() {
    30 
    31             @Override
    32             public void run() {
    33                 // TODO Auto-generated method stub
    34                 try {
    35                     staticMethodSynchronized.method2();
    36                 } catch (InterruptedException e) {
    37                     // TODO Auto-generated catch block
    38                     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             @Override
    21             public void run() {
    22                 // TODO Auto-generated method stub
    23                 try {
    24                     im.comeIn();
    25                 } catch (InterruptedException e) {
    26                     // TODO Auto-generated catch block
    27                     e.printStackTrace();
    28                 }
    29             }
    30             
    31         }, "t1");
    32         Thread t2 = new Thread(new Runnable() {
    33 
    34             @Override
    35             public void run() {
    36                 // TODO Auto-generated method stub
    37                 try {
    38                     im.comeInIn();
    39                 } catch (InterruptedException e) {
    40                     // TODO Auto-generated catch block
    41                     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             @Override
    25             public void run() {
    26                 // TODO Auto-generated method stub
    27                 try {
    28                     staticMethodSynchronized.method1();
    29                 } catch (InterruptedException e) {
    30                     // TODO Auto-generated catch block
    31                     e.printStackTrace();
    32                 }
    33             }
    34             
    35         }, "t1");
    36         Thread t2 = new Thread(new Runnable() {
    37 
    38             @Override
    39             public void run() {
    40                 // TODO Auto-generated method stub
    41                 try {
    42                     staticMethodSynchronized.method2();
    43                 } catch (InterruptedException e) {
    44                     // TODO Auto-generated catch block
    45                     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 同步块之前,我们只需判断其需要获得的对象的锁是否可获得,就可判断此方法或同步块是否可得到执行。

  • 相关阅读:
    cnpm 安装和 command not found
    C#-弄懂泛型和协变、逆变
    2019年阅读
    ES5和ES6数组方法
    ASP.NET MVC中的捆绑和压缩技术
    markdown解析与着色
    Oauth2.0
    同源策略和跨域的解决方案
    windows常用命令
    Java 9 在win10环境搭建
  • 原文地址:https://www.cnblogs.com/Michaelwjw/p/5933687.html
Copyright © 2020-2023  润新知