`
greemranqq
  • 浏览: 965196 次
  • 性别: 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  
静态方法上加锁锁的是类对象,非静态锁的是实例对象。同一个类也可能有多个类对象。

相关推荐

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

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

    项目中用到的jdbc连接 单例模式

    * 双重检查加锁方法,由于JVM内部的问题,该方法还是有漏洞的。 * 假设线程1进入到步骤2,执行步骤3未完成,会先将instance设为非null值。 * 这时候线程2会在判断instance==null的时候失败,返回一个不完整的...

    Linux系统编程之线程同步

    1. 静态初始化:如果互斥锁 mutex 是静态分配的(定义在全局,或加了static关键字修饰),可以直接使用宏进行初始化。e.g. pthead_mutex_t muetx = PTHREAD_MUTEX_INITIALIZER; 2. 动态初始化:局部变量应采用动态...

    word源码java-sync:同步

    在非静态方法里需要传入的对象 this 在静态方法里需要传入的对象 类名.class 一个对象的实例,指向其实例的变量应当被static修饰。 问题 java当中的锁 公平锁、非公平锁、读写锁、共享锁、互斥锁、自旋锁、偏向锁、...

    Java核心技术II(第8版)

    11.1.4 调用脚本的函数和方法 11.1.5 编译脚本 11.1.6 一个示例:用脚本处理GUI事件 11.2 编译器API 11.2.1 编译便捷之法 11.2.2 使用编译工具 11.2.3 一个示例:动态Java代码生成 11.3 使用注解 11.3.1 一个示例:...

    Java并发编程实战

    如何识别可并行执行的任务,如何提高单线程子系统的响应性,如何确保并发程序执行预期任务,如何提高并发代码的性能和可伸缩性等内容,最后介绍了一些高级主题,如显式锁、原子变量、非阻塞算法以及如何开发自定义的...

    perl语言脚本文档说明

    17.1.1 检索一个静态Web页 196 17.1.2 动态Web页—使用CGI 197 17.2 不要跳过这一节内容 198 17.3 编写你的第一个CGI程序 199 17.3.1 在服务器上安装CGI程序 200 17.3.2 运行你的CGI程序 201 17.4 CGI程序无法...

    24日学好Perl语言

    17.1.1 检索一个静态Web页 196 17.1.2 动态Web页—使用CGI 197 17.2 不要跳过这一节内容 198 17.3 编写你的第一个CGI程序 199 17.3.1 在服务器上安装CGI程序 200 17.3.2 运行你的CGI程序 201 17.4 CGI程序无法运行时...

    perl学习文档

    17.1.1 检索一个静态Web页 196 17.1.2 动态Web页—使用CGI 197 17.2 不要跳过这一节内容 198 17.3 编写你的第一个CGI程序 199 17.3.1 在服务器上安装CGI程序 200 17.3.2 运行你的CGI程序 201 17.4 CGI程序无法运行时...

    PERL编程24学时教程.pdf

    17.1.1 检索一个静态Web页 196 17.1.2 动态Web页—使用CGI 197 17.2 不要跳过这一节内容 198 17.3 编写你的第一个CGI程序 199 17.3.1 在服务器上安装CGI程序 200 17.3.2 运行你的CGI程序 201 17.4 CGI程序无法运行时...

    PERL编程24学时教程

    17.1.1 检索一个静态Web页 196 17.1.2 动态Web页—使用CGI 197 17.2 不要跳过这一节内容 198 17.3 编写你的第一个CGI程序 199 17.3.1 在服务器上安装CGI程序 200 17.3.2 运行你的CGI程序 201 17.4 CGI程序无法运行时...

    Perl编程24学时教程(PDF格式,共24章)

    17.1.1 检索一个静态Web页 196 17.1.2 动态Web页—使用CGI 197 17.2 不要跳过这一节内容 198 17.3 编写你的第一个CGI程序 199 17.3.1 在服务器上安装CGI程序 200 17.3.2 运行你的CGI程序 201 17.4 CGI程序无法运行时...

    perl编程24学时教程.rar

    17.1.1 检索一个静态Web页 196 17.1.2 动态Web页—使用CGI 197 17.2 不要跳过这一节内容 198 17.3 编写你的第一个CGI程序 199 17.3.1 在服务器上安装CGI程序 200 17.3.2 运行你的CGI程序 201 17.4 CGI程序无法...

    计算机操作系统作业二.doc

    信号量及wait、signal操作 加锁与解锁 信箱方式 消息缓冲方式 特权指令方式 A. 、 与 B. 与 C. 与 D. 与 24.在操作系统中,每个进程具有独立性,进程之间又具有相互制约性。对于任何两个 并发进程,它们___C___。 A....

    《计算机操作系统》期末复习指导

    (2)产生死锁的四个必要条件是资源互斥使用、保持和等待、非剥夺性、循环等待。 (3)解决死锁的方法 一般有死锁的预防,即破坏产生死锁的四个必要条件中的一个或多个,使系统绝不会进入死锁状态;死锁...

    oracle学习文档 笔记 全面 深刻 详细 通俗易懂 doc word格式 清晰 连接字符串

    日期类型 date 7字节 用于存储表中的日期和时间数据,取值范围是公元前4712年1月1日至公元9999年12月31日,7个字节分别表示世纪、年、月、日、时、分和秒 二进制数据类型 row 1~2000字节 可变长二进制数据,在具体...

    KODExplorer 芒果云-资源管理器

    - session key 加入kod_前缀 避免和其他系统key冲突 - 编辑器选中优化 选择鼠标到窗口外事件处理 ####fix bug:(bug解决和程序优化) - backspace后退截获浏览器事件,作为后退前一次访问的文件夹; - 搜索首字母...

Global site tag (gtag.js) - Google Analytics