`
greemranqq
  • 浏览: 974705 次
  • 性别: Icon_minigender_1
  • 来自: 重庆
社区版块
存档分类
最新评论

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

阅读更多

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

先看方法:

 

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

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

 

public class Walk {
	public static int num = 100;
	public static Walk walk = new Walk();
	// 静态
	public synchronized static   int run(){
			int i = 0;
			while (i < 10) {
				try {
					num --;
					i++;
					System.out.println(Thread.currentThread().getName()+":"+num);
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			return num ;
	}
	// 非静态
	public  synchronized int  walk(){
			int i = 0;
			while (i < 10) {
				try {
					num --;
					i++;
					System.out.println(Thread.currentThread().getName()+":"+num);
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			return num ;
	}
}

// 先建立两个测试类,这里我们默认循环10次
public class T3 implements Runnable {
	@Override
	public void run() {
		Walk walk = new Walk();
		//Walk walk = Walk.walk;
		walk.walk();
	}
}

public class T1 implements Runnable{
	@Override
	public void run() {
		Walk walk = new Walk();
		//Walk walk = Walk.walk;
		// 这里我依然用的new
		walk.run();
	}
}

 

// 测试方法
public class Test {
	public static void main(String[] args) {
		Thread t1 = new  Thread(new T1());
		Thread t3 = new  Thread(new T3());
		ExecutorService es = Executors.newCachedThreadPool();
		es.execute(t1);
		es.execute(t3);
		es.shutdown();
	}
}

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

 

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 在创建对象的时候,默认会给每个对象一把唯一的对象锁,一把钥匙

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

 

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

 

 

// 静态,这里仅仅将方法所 变成了 类锁。
	public  static int run(){
		synchronized(Walk.class) {
			int i = 0;
			while (i < 10) {
				try {
					num --;
					i++;
					System.out.println(Thread.currentThread().getName()+":"+num);
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			return num ;
		}
	}

 

 

结果:

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

 

 

 

 

2
1
分享到:
评论
9 楼 greemranqq 2013-11-14  
maceaykk 写道
我就不说啥了,调用两个不通的方法,加不加锁有毛用!

额,在不同的方法上加锁,不行吗?JDK 比如hashtable 不是也是在不同的方法加锁?
8 楼 greemranqq 2013-11-14  
evanzzy 写道
建立一个String对象当锁用,多踏实!

是的,也可以建立0空间的字节数组对象。
7 楼 greemranqq 2013-11-14  
skzr.org 写道
我认为清晰容易的理解:
  • 放在静态方法上的,监视器就是类对象;
  • 放在非静态方法上的,监视器就是this

谢谢哥们的总结,非常好,我也是便于理解才做得小测试。关于 静态方法上的锁,也就是所谓的类的对象,这里我其实还有疑问,就是这个锁是对象存在才回存在,而通过类直接调用静态方法的时候,这里内存会分配这个类对象吗?因为类本身没有实例化,而静态方法会在JVM方法区存在一份,这时候JVM加载的类以一种什么方式存在,或者和静态方法产生联系的呢?
6 楼 skzr.org 2013-11-13  
我认为清晰容易的理解:
  • 放在静态方法上的,监视器就是类对象;
  • 放在非静态方法上的,监视器就是this
5 楼 宋建勇 2013-11-13  
总结的相当不错! 进一步验证了我心中所想!超赞!
4 楼 evanzzy 2013-11-13  
建立一个String对象当锁用,多踏实!
3 楼 maceaykk 2013-11-13  
我就不说啥了,调用两个不通的方法,加不加锁有毛用!
2 楼 zhoujiangzi 2013-11-13  
这就是要看锁加在哪个对象上面
1 楼 teasp 2013-11-13  
静态方法上加锁锁的是类对象,非静态锁的是实例对象。同一个类也可能有多个类对象。

相关推荐

    基于synchronized修饰静态和非静态方法

    基于synchronized修饰静态和非静态方法 synchronized关键字是Java语言中的一种同步机制,用于解决多线程并发问题。在Java中,synchronized可以修饰静态方法和非静态方法,两者的锁机制不同。 非静态方法 ...

    synchronized的几种示例

    本文将深入探讨`synchronized`的几种使用示例,包括方法加锁、代码块加锁(针对`this`和对象)以及静态方法加锁。 1. **方法加锁** 当在实例方法前使用`synchronized`关键字时,它会锁定当前对象的监视器,只有...

    java中静态代码块与构造方法的执行顺序判断

    4. 初始化非静态变量。 5. 执行构造代码块(如果有的话)。 关于方法重写(Override)和重载(Overload): - 重载发生在同一类中,方法名相同,但参数列表不同(参数类型、个数或顺序不同)。 - 重写发生在父类和...

    Java双重检查加锁单例模式的详解

    在实际应用中,我们可以使用其他单例模式实现方法,例如枚举单例模式或静态内部类单例模式,这些方法可以避免DCL中的问题。但是,在使用这些方法时,我们仍然需要注意Java内存模型中的问题。 Java双重检查加锁单例...

    Java多线程-同步机制解决线程安全问题方式一:同步代码块

    2. 示例2 - 非静态方法加锁 ```java public synchronized void saleOneTicket() { // 锁对象是this,即当前对象 if (ticket &gt; 0) { System.out.println(Thread.currentThread().getName() + "卖出一张票,票号:...

    QT单例模式、多线程、双重校验加锁

    QT库是Qt公司开发的一款强大的跨平台应用程序开发框架,它提供了丰富的API用于创建GUI和非GUI应用程序。在QT中,单例模式、多线程以及双重校验加锁(Double-Checked Locking)是常见的编程模式和技术,尤其在处理...

    大厂真题之字节跳动-Java初级

    synchronized 可以修饰的作用域如下: - 非静态方法(加的锁为对象锁); - 静态方法(加的锁为类锁); - 代码块(对象锁与类锁均可); 2. Lock 采用 lock()对代码加锁,unlock()进行解锁 参考文章:...

    【技术分享】Java中的那些“锁”事.pptx

    // 静态方法加锁 synchronized static void methodName() { // 业务代码 } ``` 2. `Lock`接口:提供更灵活的锁控制,需要手动获取和释放锁。`ReentrantLock`是`Lock`的一个实现,支持可重入、公平性和非公平性等...

    Java后端资料,面试专题

    6. **静态与非静态**:在Java中,静态方法属于类,而非静态方法属于对象。因此,静态方法无法访问非静态变量,因为非静态变量是与对象实例绑定的。 7. **ATM机设计**:设计ATM机系统需要考虑事务处理、并发控制(如...

    大厂真题之字节跳动-Java初级.pdf

    - **synchronized**:Java中用于实现线程同步的关键字,可以作用于非静态方法、静态方法和代码块。非静态方法加的锁是对象锁,静态方法加的锁是类锁。代码块中的锁可以是对象锁或类锁。 - **Lock接口**:提供更...

    JAVA 面试 问题和答案

    3.4 同步方法和同步块的区别:同步方法自动对整个方法加锁,而同步块允许在特定的代码块上加锁,使得锁的范围更小,更灵活。 3.5 线程同步如何在监视器中发生?线程同步发生在对象的监视器上,确保同一时间只有一个...

    互联网大厂Java初级工程师岗位面试真题

    - **synchronized**:Java中的同步关键字,可以用于非静态方法、静态方法和代码块。对非静态方法的锁定是对象锁,对静态方法的锁定是类锁。它提供了一种互斥访问,确保同一时刻只有一个线程执行特定代码段。 - **...

    每日一题Java方向选择题答案day011

    在类的实例方法中,可以直接访问实例变量和非静态方法,但不能直接访问静态方法或静态变量,因为静态成员属于类而不是实例。题目中给出了一个类A及其成员变量和方法的定义,询问了哪些调用是错误的,这涉及到对Java...

    设计模式面试专题及答案.pdf

    5. **静态方法与非静态变量**:在Java中,静态方法属于类,不依赖于类的实例,因此不能访问非静态变量,因为非静态变量是与特定对象实例关联的。 6. **ATM机设计**:设计ATM机时,应考虑其核心功能,如账户管理、...

    简单了解Java synchronized关键字同步

    静态同步方法使用的都是同一把锁---该类对象本身,这两把锁使用的是不同的对象,所以,类的非静态方法和静态方法之间不存在锁竞争。但是一个类的静态方法之间存在锁竞争。 同步块的锁 同步块的锁是可以选择的,...

    java 程序设计笔记

    - 派生子类可以继承所有非 private 的属性和方法作为自己的成员。 - 覆盖是指子类重新定义父类已有的方法,与父类完全相同的方法名、返回值和参数类型列表。 - 重载与方法覆盖不同的是,重载不要求参数类型列表...

    10道Java面试必备的设计模式面试题!.pdf

    六、静态方法和非静态变量 在 Java 中,不能从静态上下文访问非静态数据只是因为非静态变量是跟具体的对象实例关联的,而静态的却没有和任何实例关联。 七、ATM 机设计思路 设计金融系统必须知道它们应该在任何...

    java软件工程师面试宝典

    无法直接从静态方法内部调用非静态方法,因为非静态方法属于对象,而静态方法并不依赖于任何特定的对象实例。 14. **Integer与int的区别** `Integer`是一个封装类,它可以为`int`提供额外的方法和功能,比如可以...

    CGB_面试题_第一阶段.docx

    - 非静态方法可以访问静态成员,并可以调用静态方法。 #### Final 的用法 - **修饰变量** - 被 `final` 修饰的成员变量被视为常量,一旦赋值就不能改变。 - 对于基本数据类型的变量,其值不可变;对于引用类型的...

Global site tag (gtag.js) - Google Analytics