并发线程中的 synchronized 理解

必备知识:

  • 并发的概述:体现的是在单位时间内可以处理多任务交替处理问题的能力(比如1s内支持处理100个线程,则它的并发数就是100)
  • 并发的问题:不同线程同时使用同一个资源(如对象等)并发生修改时会出现数据不一致的问题,强调的是线程的安全概念
  • synchronized关键字:内置锁,可修饰 方法(含静态方法和非静态方法)、代码块
  • 对象锁类锁
  • 互斥访问:即 串行执行,无法同时执行
  • 同步访问:即 并行执行,互不影响

延伸知识:

  • 内置锁:每个锁都会有开锁和关锁的动作,而 synchronized 并不需要我们去处理开关动作,因为它的开关动作全部在 Jvm 内部完成的,即开锁关锁动作已经被内置到 jvm 中去,所以它叫 内置锁
  • 锁的本质:synchronized 锁的本质是 jvm 的 monitor 机制
  • 锁的作用:如果有一个对象拿了一个锁,其他对象也想拿这个锁就拿不到,即 互斥访问

修饰方法

  • 修饰 非静态方法:对象锁
    public synchronized void blockFunc() {}
    
      在 java 的设计思想里面,如上方法,我们想使用它的话,就必须基于实例出来的对象进行 对象.blockFunc() 调用,在这里,synchronized 的作用范围是 对象,那么它就是一个 对象锁,所以上述方法等同于:
    public synchronized(this) void blockFunc() {}
    
  • 修饰 静态方法:类锁
    public static synchronized void blockFunc() {}
    
      而使用了 static 关键字修饰的如上方法即为 静态方法,在使用的过程中,我们只要能拿得到类,就可以通过 类.blockFunc() 直接使用,在这里,synchronized 的作用范围是 ,那么它就是一个 类锁,所以上述方法等同于:
    public static synchronized(class) void blockFunc() {}
    

修饰代码块

  • 对象锁 代码块
    public void blockCode() {
        synchronized (object) {
            //TODO coding
        }
    }
    
      在这里, object 表示 对象,它可以是 this 、全局/局部 的 Object 等。当 synchronized 后面的 变量 是 对象 时,它是一个 对象锁。那么在并发场景中,同一 实例对象 被多线程使用时,如果这个 变量 是同一对象,这些代码将会发生 互斥访问 ,反之这个 变量 如果不是同一个对象,则可以 同步访问;同理,如果是不同实例对象被多线程使用,那么他们之间是不存在互斥关系的,所以必然是 同步访问
  • 类锁 代码块
    public void blockCode() {
        synchronized (class) {
            //TODO coding
        }
    }
    
      因为 类 是 唯一 的,所以只要 synchronized 后面的 变量 使用的是 ,这时候它就是一个 类锁,类 既然是唯一,那么发生的也只能是 互斥访问

结论

  对象锁 的特点:如果不同线程监视的是 同一个实例对象,那么就会发生 互斥访问,否则可以 同步访问
  类锁 的特点:如果不同线程监视的是 同一个类(包含同一个类实例出来的不同对象),只要是使用了 类锁 的代码,那么 必然互斥访问,与根据哪个实例出来的对象进行访问无关。
  判断是否 互斥的标准:锁 的 对象 是不是 同一个对象(该对象包括类和实例的对象),如果是 ,那么就 必然 是 互斥访问,如果是 实例对象,那么就需要区分是不是同一个 实例对象,如果是,那么就是 互斥访问,否则是 同步访问

示例说明

  接下来将基于上面的结论,以实际示例的输出结果进行论证:

  • 非静态方法的 对象锁

    public class DateBean {
    
        public synchronized void blockFunc1() { 
            System.out.println("DateBean.blockFunc1 被线程 " + Thread.currentThread().getName() + " 调用的开始时间:" + System.currentTimeMillis());
            try {
                Thread.sleep(10_000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("DateBean.blockFunc1 被线程 " + Thread.currentThread().getName() + " 调用的结束时间:" + System.currentTimeMillis());
        }
        
        public synchronized void blockFunc2() { 
            System.out.println("DateBean.blockFunc2 被线程 " + Thread.currentThread().getName() + " 调用的开始时间:" + System.currentTimeMillis());
            try {
                Thread.sleep(10_000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("DateBean.blockFunc2 被线程 " + Thread.currentThread().getName() + " 调用的结束时间:" + System.currentTimeMillis());
        }
        
    }
    
    1. 强调使用相同对象访问 同一个方法:dateBean.blockFunc1(),发生 互斥访问

          public static void main(String[] args) {
          
              DateBean dateBean = new DateBean();
              
              new Thread(new Runnable() {
                  @Override
                  public void run() {
                      dateBean.blockFunc1();
                  }
              }, "AThread").start();
              
              new Thread(new Runnable() {
                  @Override
                  public void run() {
                      dateBean.blockFunc1();
                  }
              }, "BThread").start();
              
          }
      

      输出结果:
      DateBean.blockFunc1 被线程 AThread 调用的开始时间:1687388503658 —①
      DateBean.blockFunc1 被线程 AThread 调用的结束时间:1687388513671 —②
      DateBean.blockFunc1 被线程 BThread 调用的开始时间:1687388513671 —③
      DateBean.blockFunc1 被线程 BThread 调用的结束时间:1687388523683 —④

      输出现象:
      ① 输出,10s 之后 ② 和 ③ 同时输出,在过 10s 后 ④ 才输出

    2. 强调使用相同对象访问不同方法:dateBean.blockFunc1()dateBean.blockFunc2(),发生 互斥访问

          public static void main(String[] args) {
          
              DateBean dateBean = new DateBean();
              
              new Thread(new Runnable() {
                  @Override
                  public void run() {
                      dateBean.blockFunc1();
                  }
              }, "AThread").start();
              
              new Thread(new Runnable() {
                  @Override
                  public void run() {
                      dateBean.blockFunc2();
                  }
              }, "BThread").start();
              
          }
      

      输出结果:
      DateBean.blockFunc1 被线程 AThread 调用的开始时间:1687414155667 —①
      DateBean.blockFunc1 被线程 AThread 调用的结束时间:1687414165668 —②
      DateBean.blockFunc2 被线程 BThread 调用的开始时间:1687414165668 —③
      DateBean.blockFunc2 被线程 BThread 调用的结束时间:1687414175670 —④

      输出现象:
      ① 输出,10s 之后 ② 和 ③ 同时输出,在过 10s 后 ④ 才输出

    1 和 2 的结论:
      被 synchronized 修饰的非静态方法,它的锁实质上是 对象锁 ,所以在不同线程中同时使用 同一个对象 访问被 synchronized 修饰的非静态方法时(无论是否同一个方法),则发生 互斥访问
    原理:
      该并发场景有且只有一个对象:dateBean,而 public synchronized void blockFunc() {} 中的 synchronized 是一个 对象锁,它的作用范围是 dateBean 对象,所以当 AThread 持有该 对象锁 时,其他并发线程就无法持有,那么就只能等待,直到这个 对象锁 被释放后,线程 BThread 持有了它之后才能执行方法内的相关逻辑。
    通俗的理解:
      比如现在只有一辆自行车,但是想骑车的有 A、B 两人,那么这个时候 A 正使用这辆自行车在骑行,B 就只能等着,直到 A 骑完并归还了自行车,B才能使用这辆自行车进行骑行。

    1. 强调使用 不同 对象的访问同一个方法:dateBeanA.blockFunc1()dateBeanB.blockFunc1() ,发生 同步访问 (不同对象访问不同方法就留给读者自己去思考和验证吧)

      public static void main(String[] args) {
      
          DateBean dateBeanA = new DateBean();
          DateBean dateBeanB = new DateBean();
          
          new Thread(new Runnable() {
              @Override
              public void run() {
                  dateBeanA.blockFunc1();
              }
          }, "AThread").start();
          
          new Thread(new Runnable() {
              @Override
              public void run() {
                  dateBeanB.blockFunc1();
                  //dateBeanB.blockFunc2();
              }
          }, "BThread").start();
          
      }
      

      输出结果:不同对象访问同一方法
      DateBean.blockFunc1 被线程 AThread 调用的开始时间:1687389856884 —①
      DateBean.blockFunc1 被线程 BThread 调用的开始时间:1687389856884 —②
      DateBean.blockFunc1 被线程 AThread 调用的结束时间:1687389866890 —③
      DateBean.blockFunc1 被线程 BThread 调用的结束时间:1687389866890 —④
      或者:不同对象访问不同方法
      DateBean.blockFunc1 被线程 AThread 调用的开始时间:1687417037316 —①
      DateBean.blockFunc2 被线程 BThread 调用的开始时间:1687417037316 —②
      DateBean.blockFunc1 被线程 AThread 调用的结束时间:1687417047322 —③
      DateBean.blockFunc2 被线程 BThread 调用的结束时间:1687417047322 —④

      输出现象:
      ① 和 ② 同时输出,10s 之后 ③ 和 ④ 同时输出

    3 的结论:
      结合 1 和 2 的结论,在不同线程中同时使用同一个类的 不同实例对象 访问被 synchronized 修饰的非静态方法时(无论是否同一个方法),发生的是 同步访问,它们各自执行各自的逻辑代码,彼此之间 互不影响。
    原理:
      该并发场景出现了 两个对象,分别是 dateBeanA dateBeanB ,线程 AThread 持有的是 dateBeanA 的 对象锁,线程 BThread 持有的是 dateBeanB 的对象锁,它们可以同时拥有各自的 锁,所以彼此之间 互不影响
    通俗的理解:
      好比现在有两辆自行车,分别分配给想骑车的 A、B 两人,那么A、B想完成骑行这件事情时,只需要各自使用自己拥有的自行车就可以完成,两者之间互不影响。

  • 静态方法的 类锁

    public class DateBean {
    
        public static synchronized void blockFunc1() { 
            System.out.println("DateBean.blockFunc1 被线程 " + Thread.currentThread().getName() + " 调用的开始时间:" + System.currentTimeMillis());
            try {
                Thread.sleep(10_000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("DateBean.blockFunc1 被线程 " + Thread.currentThread().getName() + " 调用的结束时间:" + System.currentTimeMillis());
        }
        
        public static synchronized void blockFunc2() { 
            System.out.println("DateBean.blockFunc2 被线程 " + Thread.currentThread().getName() + " 调用的开始时间:" + System.currentTimeMillis());
            try {
                Thread.sleep(10_000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("DateBean.blockFunc2 被线程 " + Thread.currentThread().getName() + " 调用的结束时间:" + System.currentTimeMillis());
        }
        
    }
    

    输出结果:
    DateBean.blockFunc1 被线程 AThread 调用的开始时间:1687414155667 —①
    DateBean.blockFunc1 被线程 AThread 调用的结束时间:1687414165668 —②
    DateBean.blockFunc1 被线程 BThread 调用的开始时间:1687414165668 —③
    DateBean.blockFunc1 被线程 BThread 调用的结束时间:1687414175670 —④
    或者:
    DateBean.blockFunc1 被线程 AThread 调用的开始时间:1687414155667 —①
    DateBean.blockFunc1 被线程 AThread 调用的结束时间:1687414165668 —②
    DateBean.blockFunc2 被线程 BThread 调用的开始时间:1687414165668 —③
    DateBean.blockFunc2 被线程 BThread 调用的结束时间:1687414175670 —④

    输出现象:
    ① 输出,10s 之后 ② 和 ③ 同时输出,在过 10s 后 ④ 才输出

    结论:
      以上类的静态方法经验证,无论是一个对象还是多个对象的任何组合访问,最终的输出结果均是一致:互斥访问
    而出现这样一致的原因即为 synchronized 是一个 类锁,它是唯一的,与具体实例出多少对象是无关的

  • 代码块的 对象锁

    public static class DateBean {
    
        public void blockCode1() {
            synchronized (this) {
                System.out.println("DateBean.blockCode1 被线程 " + Thread.currentThread().getName() + " 调用的开始时间:" + System.currentTimeMillis());
                try {
                    Thread.sleep(10_000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("DateBean.blockCode1 被线程 " + Thread.currentThread().getName() + " 调用的结束时间:" + System.currentTimeMillis());
            }
        }
    
        public void blockCode1_1() {
            synchronized (this) {
                System.out.println("DateBean.blockCode1_1 被线程 " + Thread.currentThread().getName() + " 调用的开始时间:" + System.currentTimeMillis());
                try {
                    Thread.sleep(10_000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("DateBean.blockCode1_1 被线程 " + Thread.currentThread().getName() + " 调用的结束时间:" + System.currentTimeMillis());
            }
        }
    
        private Object object = new Object();
        public void blockCode2() {
            synchronized (object) {
                System.out.println("DateBean.blockCode2 被线程 " + Thread.currentThread().getName() + " 调用的开始时间:" + System.currentTimeMillis());
                try {
                    Thread.sleep(10_000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("DateBean.blockCode2 被线程 " + Thread.currentThread().getName() + " 调用的结束时间:" + System.currentTimeMillis());
            }
        }
    
        public void blockCode2_2() {
            synchronized (object) {
                System.out.println("DateBean.blockCode2_2 被线程 " + Thread.currentThread().getName() + " 调用的开始时间:" + System.currentTimeMillis());
                try {
                    Thread.sleep(10_000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("DateBean.blockCode2_2 被线程 " + Thread.currentThread().getName() + " 调用的结束时间:" + System.currentTimeMillis());
            }
        }
        	
        public void blockCode3() {
            Object mObject = new Object();
            synchronized (mObject) {
                System.out.println("DateBean.blockCode3 被线程 " + Thread.currentThread().getName() + " 调用的开始时间:" + System.currentTimeMillis());
                try {
                    Thread.sleep(10_000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("DateBean.blockCode3 被线程 " + Thread.currentThread().getName() + " 调用的结束时间:" + System.currentTimeMillis());
            }
        }
    
        public void blockCode3_1() {
            Object mObject = new Object();
            synchronized (mObject) {
                System.out.println("DateBean.blockCode3_1 被线程 " + Thread.currentThread().getName() + " 调用的开始时间:" + System.currentTimeMillis());
                try {
                    Thread.sleep(10_000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("DateBean.blockCode3_1 被线程 " + Thread.currentThread().getName() + " 调用的结束时间:" + System.currentTimeMillis());
            }
        }
    
        public void blockCode4() {
            Integer blockCodeObject = 127;
    //        Integer blockCodeObject = 128;
            synchronized (blockCodeObject) {
                System.out.println("DateBean.blockCode4 被线程 " + Thread.currentThread().getName() + " 调用的开始时间:" + System.currentTimeMillis());
                try {
                    Thread.sleep(10_000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("DateBean.blockCode4 被线程 " + Thread.currentThread().getName() + " 调用的结束时间:" + System.currentTimeMillis());
            }
        }
    }
    
    1. 强调使用 相同 对象的 同一个方法:dateBean.blockCode1(),发生 互斥访问

      public static void main(String[] args) {
      
          DateBean dateBean = new DateBean();
          new Thread(new Runnable() {
              @Override
              public void run() {
                  dateBean.blockCode1();
              }
          }, "AThread").start();
      
          new Thread(new Runnable() {
              @Override
              public void run() {
                  dateBean.blockCode1();
              }
          }, "BThread").start();
      }
      

      输出结果:
      DateBean.blockCode1 被线程 AThread 调用的开始时间:1687519230610 —①
      DateBean.blockCode1 被线程 AThread 调用的结束时间:1687519240612 —②
      DateBean.blockCode1 被线程 BThread 调用的开始时间:1687519240612 —③
      DateBean.blockCode1 被线程 BThread 调用的结束时间:1687519250616 —④

      输出现象:
      ① 输出,10s 之后 ② 和 ③ 同时输出,在过 10s 后 ④ 才输出

    2. 强调使用 相同 对象的 同一个方法:dateBean.blockCode2(),发生 互斥访问

      public static void main(String[] args) {
      
          DateBean dateBean = new DateBean();
          new Thread(new Runnable() {
              @Override
              public void run() {
                  dateBean.blockCode2();
              }
          }, "AThread").start();
      
          new Thread(new Runnable() {
              @Override
              public void run() {
                  dateBean.blockCode2();
              }
          }, "BThread").start();
      }
      

      输出结果:
      DateBean.blockCode2 被线程 AThread 调用的开始时间:1687519525480 —①
      DateBean.blockCode2 被线程 AThread 调用的结束时间:1687519535481 —②
      DateBean.blockCode2 被线程 BThread 调用的开始时间:1687519535481 —③
      DateBean.blockCode2 被线程 BThread 调用的结束时间:1687519545491 —④

      输出现象:
      ① 输出,10s 之后 ② 和 ③ 同时输出,在过 10s 后 ④ 才输出

    3. 强调使用 相同 对象的 不同方法:dateBean.blockCode1()dateBean.blockCode1_1(),发生 互斥访问

      public static void main(String[] args) {
      
          DateBean dateBean = new DateBean();
          new Thread(new Runnable() {
              @Override
              public void run() {
                  dateBean.blockCode1();
              }
          }, "AThread").start();
      
          new Thread(new Runnable() {
              @Override
              public void run() {
                  dateBean.blockCode1_1();
              }
          }, "BThread").start();
      }
      

      输出结果:
      DateBean.blockCode1 被线程 AThread 调用的开始时间:1687519525480 —①
      DateBean.blockCode1_1 被线程 AThread 调用的结束时间:1687519535481 —②
      DateBean.blockCode1 被线程 BThread 调用的开始时间:1687519535481 —③
      DateBean.blockCode1_1 被线程 BThread 调用的结束时间:1687519545491 —④

      输出现象:
      ① 输出,10s 之后 ② 和 ③ 同时输出,在过 10s 后 ④ 才输出

    4. 强调使用 相同 对象的 不同方法:dateBean.blockCode2()dateBean.blockCode2_2(),发生 互斥访问

      public static void main(String[] args) {
      
          DateBean dateBean = new DateBean();
          new Thread(new Runnable() {
              @Override
              public void run() {
                  dateBean.blockCode2();
              }
          }, "AThread").start();
      
          new Thread(new Runnable() {
              @Override
              public void run() {
                  dateBean.blockCode2_2();
              }
          }, "BThread").start();
      }
      

      输出结果:
      DateBean.blockCode2 被线程 AThread 调用的开始时间:1687519525480 —①
      DateBean.blockCode2_2 被线程 AThread 调用的结束时间:1687519535481 —②
      DateBean.blockCode2 被线程 BThread 调用的开始时间:1687519535481 —③
      DateBean.blockCode2_2 被线程 BThread 调用的结束时间:1687519545491 —④

      输出现象:
      ① 输出,10s 之后 ② 和 ③ 同时输出,在过 10s 后 ④ 才输出

    1、2、3 和 4 的结论:
      因为 synchronized 的变量是 this 和 实例出来的全局变量(对象),所以这时候它是一个对象锁:this 代表的是 dateBean 对象,object 代表的是 dateBean 对象中的对象,在同一个 dateBean 中它也是唯一的。 所以被修饰的 方法块 的作用范围自然也是 对象,所以它就是 对象锁 ,那么在不同线程中同时对使用 同一个对象 的 同一个代码块 或者 不同代码块 进行访问时,则 发生了互斥访问
    原理:
      该并发场景有且只有一个对象,那就是 dateBean ,而 blockCode() 方法中 synchronized (blockCodeObject) {} 方法块 的 synchronized 是一个 对象锁,它的作用范围就仅仅只是 dateBean 对象,所以当 AThread 持有该 对象锁 时,其他并发线程就无法持有,那么就只能等待,直到这个 对象锁 被释放后,线程 BThread 持有了它之后才能执行 方法块内 的相关的逻辑。
    通俗的理解:
      比如现在只有一辆自行车,但是想骑车的有 A、B 两人,那么这个时候 A 正使用这辆自行车在骑行,B 就只能等着,直到 A 骑完并归还了自行车,B才能使用这辆自行车进行骑行。

    1. 强调使用 相同 对象的不同方法:dateBean.blockCode2()dateBean.blockCode3(),发生 同步访问

      public static void main(String[] args) {
      
          DateBean dateBean = new DateBean();
          
          new Thread(new Runnable() {
              @Override
              public void run() {
                  dateBean.blockCode2();
              }
          }, "AThread").start();
          
          new Thread(new Runnable() {
              @Override
              public void run() {
                  dateBean.blockCode3();
              }
          }, "BThread").start();
      }
      

      输出结果:
      DateBean.blockCode2 被线程 AThread 调用的开始时间:1687676937120 —①
      DateBean.blockCode3 被线程 BThread 调用的开始时间:1687676937120 —②
      DateBean.blockCode2 被线程 AThread 调用的结束时间:1687676947133 —③
      DateBean.blockCode3 被线程 BThread 调用的结束时间:1687676947133 —④

      输出现象:
      ① 和 ② 同时输出,10s 之后 ③ 和 ④ 同时输出

    2. 强调使用 相同 对象的不同方法:dateBean.blockCode3()dateBean.blockCode3_1(),发生 同步访问

      public static void main(String[] args) {
      
          DateBean dateBean = new DateBean();
          
          new Thread(new Runnable() {
              @Override
              public void run() {
                  dateBean.blockCode3();
              }
          }, "AThread").start();
          
          new Thread(new Runnable() {
              @Override
              public void run() {
                  dateBean.blockCode3_1();
              }
          }, "BThread").start();
      }
      

      输出结果:
      DateBean.blockCode3 被线程 AThread 调用的开始时间:1687676937120 —①
      DateBean.blockCode4 被线程 BThread 调用的开始时间:1687676937120 —②
      DateBean.blockCode3 被线程 AThread 调用的结束时间:1687676947133 —③
      DateBean.blockCode4 被线程 BThread 调用的结束时间:1687676947133 —④

      输出现象:
      ① 和 ② 同时输出,10s 之后 ③ 和 ④ 同时输出

      5 和 6 的结论:
        不管 5(全局对象+局部对象) 还是 6(各自方法内的局部对象) 示例中,synchronized 的 变量 都不是同一个对象,所以它们之间发生的是 同步访问

    3. 同理,在强调使用 不同 对象的 同一个方法 或者 不同方法中,因为对象锁已经因为不同对象而不同了,所以发生的 同步访问

    4. 强调使用 相同 对象的 同一个方法:dateBean.blockCode4()
        在此,如果小伙伴足够细心的话,则会发现在 blockCode4() 中局部 常量对象 blockCodeObject 有两种赋值,分别是 127128 ,目前 128 是被注释状态,这里并不是我忘记删掉,而是特意留着的一处风景:

      public static void main(String[] args) {
      
          DateBean dateBean = new DateBean();
          
          new Thread(new Runnable() {
              @Override
              public void run() {
                  dateBean.blockCode4();
              }
          }, "AThread").start();
          new Thread(new Runnable() {
              @Override
              public void run() {
                  dateBean.blockCode4();
              }
          }, "BThread").start();
      }
      

      输出结果:Integer blockCodeObject = 127 异步访问
      DateBean.blockCode4 被线程 AThread 调用的开始时间:1687519525480 —①
      DateBean.blockCode4 被线程 AThread 调用的结束时间:1687519535481 —②
      DateBean.blockCode4 被线程 BThread 调用的开始时间:1687519535481 —③
      DateBean.blockCode4 被线程 BThread 调用的结束时间:1687519545491 —④

      输出现象:
      ① 输出,10s 之后 ② 和 ③ 同时输出,在过 10s 后 ④ 才输出

      输出结果:Integer blockCodeObject = 128 同步访问
      DateBean.blockCode4 被线程 AThread 调用的开始时间:1687676937120 —①
      DateBean.blockCode4 被线程 BThread 调用的开始时间:1687676937120 —②
      DateBean.blockCode4 被线程 AThread 调用的结束时间:1687676947133 —③
      DateBean.blockCode4 被线程 BThread 调用的结束时间:1687676947133 —④

      输出现象:
      ① 和 ② 同时输出,10s 之后 ③ 和 ④ 同时输出

      结论:
        blockCode4() 的并发情况会因为 Integer 的值而发生差异结果,原因在于 Integer 的取值范围是 -128 - 127,在这个范围内,它是一个常量值,它存在于一个常量池中,所有 Integer 值都指向该池中的同一个常量,所以实质上是同一个对象,所以发生的是 互斥访问;当 Integer 值为 128 时,它实际执行的是一个 new 动作申请出来的对象,那么每次执行 new 动作时候,虽然值都是 128,但是它们并不是相同对象,所以发生了 同步访问

  • 代码块的 类锁

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值