Java 彻底弄明白synchronized的使用

多个线程访问共享资源(临界资源)的时候,会出现线程安全问题,安全问题大多数是可见性和原子性问题。但这样说可能并不严谨,线程的安全性可能更在于他对错误性的定义,当多个线程访问一个类时,如果可以需要考虑运行时环境的调度和交换,并且需要额外的同步保证结果正确,我们认为这个线程是有线程安全性问题的。下面我们讨论一下可见性和原子性带来的线程安全问题。

可见性的问题

例如执行多个线程执行a++,那么多个线程就会被分配到不同的处理器上,每个处理器都从主存上复制操作一份拷贝,处理完成后复制给主存。由于分配到了不同的处理器上,两个线程的操作可能会互相覆盖,这样的结果就会和预想的又偏差。例如如下代码:

private volatile static int a=0;
	private static void add(){
		a++;
	}
	
	public static void main(String[] args) throws InterruptedException {
		// TODO Auto-generated method stub
		for(int i=0;i<1000;i++){
			new Thread(new Runnable() {
				
				@Override
				public void run() {
					add();
				}
			}).start();
		}
		System.out.println(a);
			
	}

上面的例子中,我们期望是1000,然而他却有时候输出997,998的结果,这是因为线程A执行这个操作之后,并没有返回过去,导致线程B访问的时候,访问到的不是最新的数据。当然上面也有原子性的问题,不过分开讨论。

原子性分解的问题

原子性就是不可分割的操作,在硬件层面上是指不被线程调度器中断,也就是说一个操作要么执行完,要么不执行。上面的例子中a++就不是原子性操作,首先要读取X的值,然后+1,最后写入工作内存中。三个步骤任何一部打断,都会影响最终的结果。但是a=1和return就是原子性操作了。

synchronized关键字

为了解决上述的原子性和可见性带来的线程安全问题,Java提供了同步机制互斥锁机制,这个机制保证了在同一时间内只有一个线程能访问共享资源(临界资源)。这个机制的保障来源于监视锁Monitor,在Java中,每个对象都自带监视锁,当我们要访问用synchronized修饰的方法或代码块的时候,都意味着进入这个方法或者代码块要加锁,离开要放锁。而且Synchronizd可以显示的说明对哪个对象加锁,如下例子:
synchronized public void add(){
	a++;
}
// 等价于
public void add(){
	synchronized(this){
		a++;
	}
}

对同步机制互斥锁小结

1、每个对象有自己的监视锁Monitor,这意味着多个线程访问一个对象的Synchronizd方法或者代码块时,需要等其他线程放锁才可访问。相对的多个对象的监视锁不存在互斥情况。
2、互斥机制只能保证Synchronizd代码块里的代码同步,而不再代码块里的代码不能得到保证。
3、子类重写父类的Synchronizd方法不能保证同步。因为Synchronizd并不属于方法定义的一部分。
4、SYnchronizd修饰静态方法时,等同于给该类的所有对象都加了一把锁。因为静态方法属于类,不属于对象。
5,Synchronizd把类当监视锁的话,效果等同4.
5、Synchronizd修饰的方法的监视锁(Monitor)也是重入锁,也就是说当一个线程获得监视锁之后,可以再次获得该对象的锁。例如,线程A调用一个对象的同步方法之后,可以调用该对象的另一个同步方法。




参考资料:

Java并发开发实战

兰亭风雨:http://blog.csdn.net/ns_code/article/details/17199201

阳光日志:http://blog.csdn.net/luoweifu/article/details/46613015

海子:http://www.cnblogs.com/dolphin0520/p/3923737.html


  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中的synchronized关键字用于修饰方法或代码块,它能够保证在同一时刻最多只有一个线程执行该段代码。 通过在方法或代码块前使用synchronized关键字,可以实现对共享资源的互斥访问,避免多个线程同时修改共享资源导致的数据不一致问题。 使用synchronized修饰方法时,当一个线程进入该方法后,会获得该方法所属对象的锁,其他线程则需要等待锁的释放才能进入该方法。这样可以确保同一时刻最多只有一个线程执行该方法。 使用synchronized修饰代码块时,需要指定一个对象作为锁,当一个线程进入这个代码块时,会获得该锁,其他线程需要等待锁的释放才能进入代码块。这样可以确保同一时刻最多只有一个线程执行该代码块。 在多线程编程中,使用synchronized关键字可以有效地在多个线程之间实现同步,避免数据竞争和并发错误的发生。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [JavaSynchronized的用法(简单介绍)](https://blog.csdn.net/NeiHan2020/article/details/123277175)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [Javasynchronized的用法详解(四种用法)](https://blog.csdn.net/SimpleGZW/article/details/119866588)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值