静态方法加锁,和非静态方法加锁区别

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

先看方法:

// 这是一个很简单的类,里面共享静态变量 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. // 先建立两个测试类,这里我们默认循环10次
  36. public class T3 implements Runnable {
  37. @Override
  38. public void run() {
  39. Walk walk = new Walk();
  40. //Walk walk = Walk.walk;
  41. walk.walk();
  42. }
  43. }
  44. public class T1 implements Runnable{
  45. @Override
  46. public void run() {
  47. Walk walk = new Walk();
  48. //Walk walk = Walk.walk;
  49. // 这里我依然用的new
  50. walk.run();
  51. }
  52. }

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 锁效果一样,直接属于类的

时间: 2024-10-09 23:45:32

静态方法加锁,和非静态方法加锁区别的相关文章

java synchronized同步静态方法和同步非静态方法的区别与举例

synchronized关键字是java并发编程中为了解决线程对共享资源的竞争造成错误,而提供的解决方案.synchronized关键字有两种用法,一种是只用于方法的定义中,另外一种是synchronized块,我们不仅可以使用synchronized来同步一个对象变量,你也可以通synchronized来同步类中的静态方法和非静态方法.那么问题来了,同步静态方法与动态方法有什么区别呢?看完下面这个例子或许你就明白了. public class test2 {  public static in

PHP中静态方法(static)与非静态方法的使用及区别

static关键字用来修饰属性.方法,称这些属性.方法为静态属性.静态方法. static关键字声明一个属性或方法是和类相关的,而不是和类的某个特定的实例相关,因此,这类属性或方法也称为"类属性"或"类方法" 如果访问控制权限允许,可不必创建该类对象而直接使用类名加两个冒号"::"调用. static关键字可以用来修饰变量.方法. 不经过实例化,就可以直接访问类中static的属性和static的方法. static 的属性和方法,只能访问sta

java静态方法(变量)、非静态方法(变量)区别

java静态方法.静态变量在调用时生成唯一标识,即在内存中给定一个静态位子,这样在调用时可以直接找到,而且会节省内存.但如果声明的静态方法.静态变量过多,会占用过多内存,有可能导致内存溢出. 非静态方法.非静态变量有gc管理,每new一个对象时,在堆上分配一块内存,不用时由gc回收. 即,静态方法.静态变量预先分配,非静态方法.非静态变量动态分配. 所有的class.static变量位于方法区,方法区既可以在堆上,又可以在栈上.

静态方法中调用非静态方法

静态static方法中不能调用非静态non-static方法,准确地说是不能直接调用non-static方法.但是可以通过将一个对象的引用传入static方法中,再去调用该对象的non-static方法. 在主函数(static方法)中我们经常创建某个类的实例,再利用其引用变量调用它的非静态方法. //StaticMethodTest.java//A ststic method cannot call a non-static method, but we can transfer a obje

转:静态方法中调用非静态方法

我们都知道,静态static方法中不能调用非静态non-static方法,准确地说是不能直接调用non-static方法.但是可以通过将一个对象的引用传入static方法中,再去调用该对象的non-static方法. 其实这个事实的应用很经常,以至于我们不去重视:在主函数(static方法)中我们经常创建某个类的实例,再利用其饮用变量调用它的非静态方法. //StaticMethodTest.java//A ststic method cannot call a non-static metho

静态方法和非静态方法的区别

首先,两者本质上的区别是:静态方法是在类中使用staitc修饰的方法,在类定义的时候已经被装载和分配.而非静态方法是不加static关键字的方法,在类定义时没有占用内存,只有在类被实例化成对象时,对象调用该方法才被分配内存. 其次,静态方法中只能调用静态成员或者方法,不能调用非静态方法或者非静态成员,而非静态方法既可以调用静态成员或者方法又可以调用其他的非静态成员或者方法. 例子1:静态方法的Main方法访问类中的非静态成员方法. class Test{ public int sum(int a

&lt;转&gt;java中静态方法和非静态方法的存储

Java中非静态方法是否共用同一块内存? 将某 class 产生出一个 instance 之后,此 class 所有的 instance field 都会新增一份,那么所有的 instance method 是否也会新增一份?答案是不会,我们用field表示字段,用method表示方法,那么加上static区分后就 有四种: class field:有用static修饰的fieldclass method:有用static修饰的methodinstance field:没有用static修饰的f

探究委托的如何实现非静态方法

在C#里面对于委托的如何加载非静态方法一直都很疑惑,自己对于非静态方法的认识来看,如果要安全的实现非静态方法必须引用实例里面的字段,经过查阅资料,知道委托类里面有一个_target字段如果是委托的是静态方法值为零,如果委托是非静态为会自动寻找方法的实例,说的很模糊,这个_target字段应该是一个object型,但是里面地址指向的什么.我先在程序上声明一个类有两个字段这两个字段一个为static一个非static,有两个方法一个静态一个非静态,我在方法里面对类的方法进行操作,看看对实例有什么影响

java中静态方法中为什么不能使用this、super和直接调用非静态方法

这个要从java的内存机制去分析,首先当你New 一个对象的时候,并不是先在堆中为对象开辟内存空间,而是先将类中的静态方法(带有static修饰的静态函数)的代码加载到一个叫做方法区的地方,然后再在堆内存中创建对象.所以说静态方法会随着类的加载而被加载.当你new一个对象时,该对象存在于对内存中,this关键字一般指该对象,但是如果没有new对象,而是通过类名调用该类的静态方法也可以. 程序最终都是在内存中执行,变量只有在内存中占有一席之地时才会被访问,类的静态成员(静态变量和静态方法)属于类本