zoukankan      html  css  js  c++  java
  • (转)静态方法加锁,和非静态方法加锁区别

    今天看了到有意思的题:在静态方法上加锁 和 非静态方法加锁 有什么区别,从而再次引出锁机制的一些理解。

    先看方法:

    // 这是一个很简单的类,里面共享静态变量 num,然后一个静态 和 非静态方法,都加上锁

    // 我们假设有两个线程同时操作这两个方法,那么数据能互斥吗?

    Java代码  收藏代码
    1. public class Walk {  
    2.     public static int num = 100;  
    3.     public static Walk walk = new Walk();  
    4.     // 静态  
    5.     public synchronized static   int run(){  
    6.             int i = 0;  
    7.             while (i < 10) {  
    8.                 try {  
    9.                     num --;  
    10.                     i++;  
    11.                     System.out.println(Thread.currentThread().getName()+":"+num);  
    12.                     Thread.sleep(1000);  
    13.                 } catch (InterruptedException e) {  
    14.                     e.printStackTrace();  
    15.                 }  
    16.             }  
    17.             return num ;  
    18.     }  
    19.     // 非静态  
    20.     public  synchronized int  walk(){  
    21.             int i = 0;  
    22.             while (i < 10) {  
    23.                 try {  
    24.                     num --;  
    25.                     i++;  
    26.                     System.out.println(Thread.currentThread().getName()+":"+num);  
    27.                     Thread.sleep(1000);  
    28.                 } catch (InterruptedException e) {  
    29.                     e.printStackTrace();  
    30.                 }  
    31.             }  
    32.             return num ;  
    33.     }  
    34. }  
    35.   
    36. // 先建立两个测试类,这里我们默认循环10次  
    37. public class T3 implements Runnable {  
    38.     @Override  
    39.     public void run() {  
    40.         Walk walk = new Walk();  
    41.         //Walk walk = Walk.walk;  
    42.         walk.walk();  
    43.     }  
    44. }  
    45.   
    46. public class T1 implements Runnable{  
    47.     @Override  
    48.     public void run() {  
    49.         Walk walk = new Walk();  
    50.         //Walk walk = Walk.walk;  
    51.         // 这里我依然用的new  
    52.         walk.run();  
    53.     }  
    54. }  
    Java代码  收藏代码
    1. // 测试方法  
    2. public class Test {  
    3.     public static void main(String[] args) {  
    4.         Thread t1 = new  Thread(new T1());  
    5.         Thread t3 = new  Thread(new T3());  
    6.         ExecutorService es = Executors.newCachedThreadPool();  
    7.         es.execute(t1);  
    8.         es.execute(t3);  
    9.         es.shutdown();  
    10.     }  
    11. }  

     // 测试数据 我就不完全列出了

    pool-1-thread-1:98

    pool-1-thread-2:98

    pool-1-thread-2:97

    pool-1-thread-1:96

    .....

    可以看出两个线程没有互斥,这是为什么呢?

    OK,我们将static 关键字去掉,代码我就不贴了,直接看结果。。

    pool-1-thread-1:98

    pool-1-thread-2:98

    pool-1-thread-2:96

    ... 

    结果还是没有出现互斥现象,因此我们默认要先让一个线程执行10次的,假设我们这个是买票系统这是不允许的。为什么会出现这状况呢,方法都加上的锁的。

    这里先引一下锁的理解,然后从后向前解释。

    JAVA 的锁机制说明:每个对象都有一个锁,并且是唯一的。假设分配的一个对象空间,里面有多个方法,相当于空间里面有多个小房间,如果我们把所有的小房间都加锁,因为这个对象只有一把钥匙,因此同一时间只能有一个人打开一个小房间,然后用完了还回去,再由JVM 去分配下一个获得钥匙的人。

    第二次实验,我们是对方法进行加锁了,但是没得到想要的结果,原因在于房间与钥匙。因为我们每个线程在调用方法的时候都是new 一个对象,那么就会出现两个空间,两把钥匙,而静态变量只有一个,相当于我们有两把钥匙,从不同的房间开门取共享的值,因此出错。

    如果我们使用静态变量walk 呢?这代码放开,也就是我们统一使用一个对象去操作变量,那么结果..

    使用 Walk.walk.walk();  和 Walk.run();

    结果:还是没有互斥

    pool-1-thread-1:99

    pool-1-thread-2:98

    pool-1-thread-1:97

    ...

    如果我们把静态方法关键字 去掉: 就可以看见互斥现象了

    pool-1-thread-1:99

    pool-1-thread-1:98

    pool-1-thread-1:96

    结果发现还是会重复,因此我们可以得出,在静态方法上加锁,和普通方法上加锁,他们用的不是同一把所,不是同一把钥匙。从而得出 他们的对象锁是不同的,对象也是不同的。

    这里再次引出一个概念:对象锁  和  类锁

    对象锁:JVM 在创建对象的时候,默认会给每个对象一把唯一的对象锁,一把钥匙

    类锁:每一个类都是一个对象,每个对象都拥有一个对象锁。

    呵呵,概念感觉混淆了,其实都是锁,取两个名词,下面区分方便,效果是一样的,如果我们这样实现。

    Java代码  收藏代码
    1. // 静态,这里仅仅将方法所 变成了 类锁。  
    2.     public  static int run(){  
    3.         synchronized(Walk.class) {  
    4.             int i = 0;  
    5.             while (i < 10) {  
    6.                 try {  
    7.                     num --;  
    8.                     i++;  
    9.                     System.out.println(Thread.currentThread().getName()+":"+num);  
    10.                     Thread.sleep(1000);  
    11.                 } catch (InterruptedException e) {  
    12.                     e.printStackTrace();  
    13.                 }  
    14.             }  
    15.             return num ;  
    16.         }  
    17.     }  

    结果:

    pool-1-thread-1:98

    pool-1-thread-2:98

    pool-1-thread-2:97

    pool-1-thread-1:97

    ...

    发现结果还是不是互斥的,说明在静态方法上加锁,和 实例方法加锁,对象锁 其实不一样的。如果我们改成:

    synchronized(walk) {

    //....略

    }

    结果:

    pool-1-thread-2:99

    pool-1-thread-2:98

    pool-1-thread-2:97

    这样就互斥了,因为T1 是通过静态变量walk 调用的,默认就是用的walk 对象这把锁,而静态方法 强制让他也使用 walk这把锁,就出现了互斥现象,因为钥匙只有一把。

    如果我们两个方法都是静态方法呢?

    ..

    小结:

        1.对象锁钥匙只能有一把才能互斥,才能保证共享变量的唯一性

        2.在静态方法上的锁,和 实例方法上的锁,默认不是同样的,如果同步需要制定两把锁一样。

        3.关于同一个类的方法上的锁,来自于调用该方法的对象,如果调用该方法的对象是相同的,那么锁必然相同,否则就不相同。比如 new A().x() 和 new A().x(),对象不同,锁不同,如果A的单利的,就能互斥。

        4.静态方法加锁,能和所有其他静态方法加锁的 进行互斥

        5.静态方法加锁,和xx.class 锁效果一样,直接属于类的

     6.同一个对象两个非静态方法分别加锁,用的是同一个对象的锁,可以实现互斥。

    更多信息:Synchronized的作用

  • 相关阅读:
    《剩女郎》的艺术魅力
    话剧《剩女郎》
    实验四
    手机app
    APP
    卡尔曼滤波 —— MATLAB实现
    卡尔曼滤波
    卡尔曼滤波
    2017 年全国大学生电子设计竞赛试题——四旋翼自主飞行器探测跟踪系统(C 题)【本科组】2
    2017 年全国大学生电子设计竞赛试题——四旋翼自主飞行器探测跟踪系统(C 题)【本科组】2
  • 原文地址:https://www.cnblogs.com/icemargin/p/12706732.html
Copyright © 2011-2022 走看看