探索JAVA中synchronized锁机制

 一段synchronized的代码被一个线程执行之前,他要先拿到执行这段代码的权限,在Java

里边就是拿到某个同步对象的锁(一个对象只有一把锁); 

如果这个时候同步对象的锁被其他线程拿走了,他(这个线程)就只能等了(线程阻塞在锁池等待队列中)。

 取到锁后,他就开始执行同步代码(被synchronized修饰的代码);线程执行完同步代码后马上就把锁还给同步对象,

其他在锁池中等待的某个线程就可以拿到锁执行同步代码了。这样就保证了同步代码在统一时刻只有一个线程在执行。

  以上这段话在一个博文中看到,之前也对java中synchronized同步锁机制搞不清楚。

今天闲来无事也写了一个例子记录一下,顺带自己理解一下JAVA

机制中静态同步和非静态同步的区别。

例子调用三个方法

第一个:使用单例模式进行访问(其中区别同步和不同步效果)

第二个:非静态同步(其中区别单例模式和非单例模式效果)

第三个:静态同步

废话不多说开始~~~

第一种方式

使用单例模式调用方式

public class ThreadStudyTest4 extends Thread{
	private String nameThread;
	public ThreadStudyTest4(String name){
		this.nameThread = name;
	}
	public void run() {
		Study1.getInstan().methodA(nameThread);
	}
	
	public static void main(String[] args) {
		ThreadStudyTest4 ta =new ThreadStudyTest4("AAAA");
		ThreadStudyTest4 tb =new ThreadStudyTest4("BBBB");
		ta.start();
		tb.start();
	}
}

首先先创建一个类,这个类是多线程要执行的。也就是会争夺锁的

        private static Study1 study= null;
	public static Study1 getInstan(){
		if(study==null){
			study = new Study1();
		}
		return study;
	}
这里我使用单例模式创建这个类,然后看下调用的方法:

	public  void methodA(String name){
		System.out.println(name+":线程进来方法了++++++++");
		int count =10;
		while(count>0){
			System.out.println(name+"xunhuan"+count);
			count--;
		}
	}
只是打印每个线程从5-1的过程,那么如果不同步的话结果是:

BBBBxunhuan5
BBBBxunhuan4
AAAAxunhuan5
BBBBxunhuan3
AAAAxunhuan4
BBBBxunhuan2
AAAAxunhuan3
BBBBxunhuan1
AAAAxunhuan2
AAAAxunhuan1

可以看到结果是乱的,我们希望是看到一个线程打印完另一个线程再打印,

那么我在方法上加同步操作也就是synchronized关键字

BBBBxunhuan5
BBBBxunhuan4
BBBBxunhuan3
BBBBxunhuan2
BBBBxunhuan1
AAAAxunhuan5
AAAAxunhuan4
AAAAxunhuan3
AAAAxunhuan2
AAAAxunhuan1

这次是正确了,(这里解释一下为何B先打印,当线程调用start()方法时线程并不是立即执行)。

那么我们开头说到一个线程获得锁后,另一个线程会等待但我们从这个结果中并不能看到等待的过程,

因此我们改造一下代码。

	public  void methodA(String name){
		System.out.println(name+":线程进来方法了++++++++");
		synchronized (object) {
			System.out.println("测试线程加锁过程++++++");
			int count =5;
			while(count>0){
				System.out.println(name+"xunhuan"+count);
				count--;
			}
		}
	}

按照刚才说的如果一个线程先执行进来后执行打印,另一个线程过来发现object被锁着就先等待,

但依然会打印"线程进来方法了+++"这段话表示它在等待

第一个线程执行完成后释放锁,那么我再来看下结果会不是是这样:

AAAA:线程进来方法了++++++++
AAAA测试线程加锁过程++++++
BBBB:线程进来方法了++++++++
AAAAxunhuan5
AAAAxunhuan4
AAAAxunhuan3
AAAAxunhuan2
AAAAxunhuan1
BBBB测试线程加锁过程++++++
BBBBxunhuan5
BBBBxunhuan4
BBBBxunhuan3
BBBBxunhuan2
BBBBxunhuan1

我们可以看到,线程A先执行,获得object锁,然后执行打印,但是这时线程B也过来执行打印但是

发现线程A为执行完毕,就进行等待,等线程A执行后释放了锁,线程B获得锁开始执行打印。

这个印证了我们起初第一段提到的锁机制。


第二种非静态同步(其中区别单例模式和非单例模式效果),

改造线程调用类,在run()方法中使用new关键字创建对象

public class ThreadStudyTest4 extends Thread{
	private String nameThread;
	public ThreadStudyTest4(String name){
		this.nameThread = name;
	}
	public void run() {
		new Study1().methodA(nameThread);
	}
	
	public static void main(String[] args) {
		ThreadStudyTest4 ta =new ThreadStudyTest4("AAAA");
		ThreadStudyTest4 tb =new ThreadStudyTest4("BBBB");
		ta.start();
		tb.start();
	}
}


当然再改造另一个被调用类,先把单例模式注释掉,把synchronized提到了方法上,但是没有加static关键字

//	private static Study1 study= null;
//	public static Study1 getInstan(){
//		if(study==null){
//			study = new Study1();
//		}
//		return study;
//	}

	public synchronized void methodB(String name){
		int count =5;
		while(count>0){
			System.out.println(name+"xunhuan"+count);
			count--;
		}
	}
这样执行后结果:

AAAAxunhuan5
BBBBxunhuan5
AAAAxunhuan4
BBBBxunhuan4
AAAAxunhuan3
BBBBxunhuan3
AAAAxunhuan2
BBBBxunhuan2
AAAAxunhuan1
BBBBxunhuan1

是乱的,也就是并没有同步成功,为什么呢?先不要着急,我们再来看下单例模式下的效果,

当然还要再改造一下刚才的代码把run()方法中new关键字改成Study1.getInstan()使用单例模式创建类,

同样把被调用类中单例模式的代码放开,再次执行:

AAAAxunhuan5
AAAAxunhuan4
AAAAxunhuan3
AAAAxunhuan2
AAAAxunhuan1
BBBBxunhuan5
BBBBxunhuan4
BBBBxunhuan3
BBBBxunhuan2
BBBBxunhuan1

正确了!!诶这是为什么呢?首先来看synchronized关键字他的原理就是使用了锁,

如果采用method级别的同步,则对象锁即为method所在的对象

那么我们第一次使用的是非单例模式,也就是使用了new关键字。

那么就是说A线程执行时创建了一个对象(也就是锁),B线程执行时也创建了锁,他们

本来锁就不是一个锁所以实现不了同步,但是我们使用单例模式时线程A和线程B使用的

是同一个对象也就是同一个锁,所以实现了同步成功!


第三种 静态同步

这里我们就不再演示单例模式和非单例模式的效果因为:

如果是静态方法,对象锁即指method所在的Class对象(唯一)

执行线程的类

public class ThreadStudyTest4 extends Thread{
	private String nameThread;
	public ThreadStudyTest4(String name){
		this.nameThread = name;
	}
	public void run() {
		new Study1().methodC(nameThread);
	}
	
	public static void main(String[] args) {
		ThreadStudyTest4 ta =new ThreadStudyTest4("AAAA");
		ThreadStudyTest4 tb =new ThreadStudyTest4("BBBB");
		ta.start();
		tb.start();
	}
}
被调用的类的方法(这里所有都注释掉,只留这一个方法)

	public static synchronized void methodC(String name){
		int count =5;
		while(count>0){
			System.out.println(name+"xunhuan"+count);
			count--;
		}
	}

我们运行结果:

BBBBxunhuan5
BBBBxunhuan4
BBBBxunhuan3
BBBBxunhuan2
BBBBxunhuan1
AAAAxunhuan5
AAAAxunhuan4
AAAAxunhuan3
AAAAxunhuan2
AAAAxunhuan1

刚才说了不区分单例模式和非单例模式,这个没有影响效果是一样的。

到这里我就记录完毕了,希望能帮助到别人吧!同样也帮助了我自己O(∩_∩)O~


参考文档:http://blog.csdn.net/yangzhijun_cau/article/details/6432216(这个文章是转的,但没有标明地址所以只能贴上这里了)







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值