synchronized方法和代码块的理解

     熟悉 Java 的多线程的一般都知道会有数据不一致的情况发生,比如两个线程在操作同一个类变量时,而保护数据不至于错乱的办法就是让方法同步或者代码块同步。同步时非原子操作就得同步,比如一个简单的 1+1 运算也该同步,以保证一个代码块或方法成为一个原子操作。

对于给在多线程环境中可能会造成数据破坏的方法,做法有两种,以及一些疑问

1. 当一个线程访问了对象的synchronized或synchronized(xxx)代码块时,非同步方法是否会阻塞?

2.不论是静态的或非静态的方法都加上 synchronized 关键字,那静态的方法和非静态的方法前加上 synchronized 关键字有区别吗?

3. 或者在可疑的代码块两旁用 synchronized(this) 或 synchronized(someObject) 包裹起来,而选用 this 还是某一个对象--someObject,又有什么不同呢?
4. 对方法加了 synchronized 关键字或用 synchronized(xxx) 包裹了代码,就一定能避免多线程环境下的数据破坏吗?

5. 对方法加 synchronized 关键字与用 synchronized(xxx) 同步代码块两种规避方法又有什么分别和联系呢?

为了理解上面的问题,我们还得了解基本的东西,就是监视区域:从 Java 对线程同步的原理上说起, Java 直接在语言级上支持多线程的,在多线程环境中我们要小心的数据是:

1) 保存在堆中的实例变量
2) 保存在方法区中的类变量。
现实点说呢就是某个方法会触及到的同一个变量,如类变量或单态实例的实例变量。避免冲突的最容易想到的办法就是同一时刻只让一个线程去执行某段代码块或方法,于是我们就要给一段代码块或整个方法体标记出来,被保护的代码块或方法体在 Java 里叫做监视区域(Monitor Region)。

知道上面的针对上面的问题,我们通过下面代码进行逐一验证:

1)对于多个线程并发访问对象来说,非同步方法不会阻塞。

2)静态和非静态方法加上synchronized的区分是,监视区域的范围,非静态方法监视范围是对象级的,而非静态方法监视范围是类级的,和我们平常认识的类方法及实例方法一样。

代码说明:

public class Test3 {

	private static int flag = 1;
	
	public  synchronized static void accum(int time){
		flag++;
		try{
			Thread.sleep(time);
		}catch(InterruptedException e){
			e.printStackTrace();
		}
		flag--;
		System.out.println("Thread: " + Thread.currentThread().getName()+ " /Current flag: " + flag);
	}
	
	public void acc(){
		System.out.println("非同步方法:"+flag);
	}
	
	public synchronized void ac(){
		flag--;
		System.out.println("ac:"+flag);
	}
	
	public static void  main(String[] args){
		/*final Test3 test = new Test3(); ---同一个对象  监视范围为对象的线程可以正常阻塞
		new Thread("one"){
			public void run(){
				test.accum(2000);
			}
		}.start();
		new Thread("two"){
			public void run(){
				test.accum(10);
			}
		}.start();*/
		/*new Thread("tt"){
			public void run(){
				test.acc();
			}
		}.start();*/
		new Thread("one"){
			public void run(){
				new Test3().accum(2000);
			}
		}.start();
		new Thread("two"){
			public void run(){
				new Test3().accum(100);
			}
		}.start();
		new Thread("tt"){
			public void run(){
				new Test().acc();
			}
		}.start();
	}
}


运行结果:

非同步方法:1
Thread: one /Current flag: 1 //等待2秒出现
Thread: two /Current flag: 1 

去掉static标识,运行结果如下:

非同步方法:1
Thread: two /Current flag: 2
 

Thread: one /Current flag: 1  //等待2秒出现

3. 或者在可疑的代码块两旁用 synchronized(this) 或 synchronized(someObject) 包裹起来,而选用 this 还是某一个对象--someObject,又有什么不同呢?

验证代码如下:

private static int flag = 1;
	private static Object obj = new Object();
	public   void accum(int time){
		synchronized(this){  ------
		flag++;
		try{
			Thread.sleep(time);
		}catch(InterruptedException e){
			e.printStackTrace();
		}
		flag--;
		System.out.println("Thread: " + Thread.currentThread().getName()+ " /Current flag: " + flag);
		}
		
	}
	
	public void acc(){
		System.out.println("非同步方法:"+flag);
	}
	
	public  void ac(){
		synchronized(this){
		flag--;
		}
		System.out.println("ac:"+flag);
	}
	
	public static void  main(String[] args){
		new Thread("one"){
			public void run(){
				new Test2().accum(1000);
			}
		}.start();
		new Thread("two"){
			public void run(){
				new Test2().accum(20);
			}
		}.start();
		
	}
运行结果:

Thread: two /Current flag: 2 //证明第2个线程没有被阻塞
Thread: one /Current flag: 1

-----对于synchronized(this)来说,其监视区域为对象,但上例中因为都是用new出来的对象,所以不同对象的监视区域不同,所以第二个线程不会被第一个线程阻塞掉

synchronized(this){  把改行代码改成synchronized(object)

 
 运行结果如下: 
Thread: one /Current flag: 1 //2秒后出现,证明第2个线程在等待中 
Thread: two /Current flag: 1
synchronized(XX.class) XX来实际的类
运行结果和synchronized(object)一样,证明了其监视区域为类级
4. 对方法加了 synchronized 关键字或用 synchronized(xxx) 包裹了代码,就一定能避免多线程环境下的数据破坏吗?
---上例已经回答了该问题,如果在new出来的不同对象上,并不能保证数据的原子性,只有监视对象为类级时才可以保证

5. 对方法加 synchronized 关键字与用 synchronized(xxx) 同步代码块两种规避方法又有什么分别和联系呢?

----给方法加个关键字 synchronized 其实就是相当于把方法中的所有代码行框到了 synchronized(xxx) 块中。同步肯定会影响到效率,这也是大家知道的,因为它会造成方法调用的等待。方法中有些代码可能是线程安全的,所以可不用包裹在 synchronized(xxx) 中,提高线程安全的额外代码执行效率。

---以上是我对ynchronized 关键字与用 synchronized(xxx) 同步代码块的一些理解~


PS:一直认为我们所接触的技术都来源于生活,一直想用生活的语言表达出来,无奈水平有限。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值