Java高并发syncronized深入理解
1.Synchronized的作用:
能够保证在同一时刻最多只有一个线程执行该段代码,以达到保证并发安全的效果。
2.地位:
1)Synchronized是java的关键字,并java的怨言原生支持;
2)最基础的互斥同步手段;
3)并发编程中的元老级角色,是并发编程的必学内容。
3.不使用并发手段会有什么后果?
(1)两个线程同时a++,最后结果会比预想的少
原因:count++实际上是有3个操作完成:
1)读取count;
2)将count加一;
3)将count的值写入到内存中。
4.Synchronized的两个用法:
(1)对象锁:包括方法锁(默认锁对象为this当前实例对象)和同步代码块锁(自己指定锁对象);
(2)类锁:指synchronized修饰静态的方法或指定锁为Class对象。
5.synchronized类锁
概念:Java类可能有很多个对象,但只有一个Class对象,类锁时Class对象的锁(类锁只能在同一时刻被一个对象拥有)
形式:
1)synchronized加在static方法上;
2)synchronized(*.class)代码块。
6.多线程访问同步方法的7种情况:
1)两个线程同时访问一个对象的同步方法;
解释:对象所。会相互等待,只能有一个线程持有锁。
2) 两个线程访问的是两个对象的同步方法;
解释:对象锁。不同的对象实例,拥有不同的对象锁,互不影响并发执行。
3) 两个线程访问的是synchronized的静态方法;
解释:类锁。
4) 同时访问同步方法与非同步方法;
解释:synchronized关键字只作用于当前方法,不会影响其他未加关键字的方法的并发行为。因此非同步方法不受到影响,还是会并发执行。
5) 访问同一个对象的不同的普通同步方法;
解释:对象锁。
synchronized关键字虽然没有指定所要的那个锁对象,但是本质上是指定了this这个对象作为它的锁。所以对于同一个实例来讲,两个方法拿到的是同一把锁,因此会出现串行的情况。
6) 同时访问静态synchronized和非静态的synchronized方法;
解释:前者为类锁,锁为Class类;后者为对象锁,锁为this对象。因此两者的锁不同,会并行执行。
7) 方法抛异常后,会释放锁。
特殊:Lock类加锁时,如果出现异常,不显式手动释放锁的话,Lock是不会释放的。
而synchronized不同,一旦出现异常,会自动释放锁。
也就是说当第二个线程等待一个被synchronized修饰的方法时,若第一个线程出现异常退出,这把锁会立刻释放并且被第二个线程所获取到。JVM会自动把锁释放。
8)扩展:线程进入到一个被synchronized修饰的方法,而在这个方法里面调用了另外一个没有被synchronized修饰的方法,这个时候还是线程安全的吗?
答案:不是的。出了本方法后,由于另外的方法没有被synchronized修饰,所以说这个方法可以被多个线程同时访问的。
7.synchronized核心思想总结:
1)一把锁同时只能被一个线程获取,没有拿到锁的线程只能等待(对应1,5);
2)每个实例对应自己的一把锁,不同实例对应不同的锁,相互不影响,可以并行。例外:如果锁是*.class以及synchronized修饰的是static方法时,即类锁时,所有对象共用一把锁(对应2,3,4,6)
3)无论是正常执行还是抛出异常,都会释放锁(对应7)
8.syscronized性质(可重入,不可中断)
1)可重入:一个线程拿到了锁,这个线程可以再次使用该锁对其他方法,说明该锁是可以重入的;
2)不可重入:一个线程拿到锁了,如果需要再次使用该锁,必须先释放该锁才能再次获取。
可重入锁的好处:
1)避免死锁 2)提升封装性
粒度:
可重入的特性是线程级别的,不是调用级别的(pthread线程)。
问题:为什么synchronized具有可重入性?
答:指的是同一线程的外层函数获得锁之后,内层函数可以直接再次获取该锁(可避免死锁,锁方法1在内部访问锁方法2,用的是同一把锁)。
什么样的可重入?
1)同一个方法是可重入的;
2)可重入不要求是同一个方法;
3)可重入不要求是同一个类中的。
synchronized的性质:不可中断性质
1)线程A拿到锁,不释放的话,别人永远拿不到锁,永远等待;
2)Lock锁会有一些比较灵活的功能,按时间等。
加锁和释放锁的原理:
现象:
每个类的实例对应着一把锁,每个syncronized方法首先必须获得调用该方法实例的锁,才能执行;否则,线程只能被阻塞。方法一旦执行,便独占了该把锁。直到该方法执行结束返回或者抛出异常,才将该锁释放。锁释放之后,其他阻塞锁才能竞争获取该把锁。
当一个对象中有synchronized修饰的方法或者代码块的时候,要想执行这段代码,就必须先获得这个对象锁,如果此对象的对象锁已经被其他调用者所占用,就必须等待它被释放。所有的Java对象都含有一个互斥锁,这个锁由JVM自动去获取和释放,我们只需要指定这个对象就行了,至于锁的释放和获取不 需要我们操心。
获取和释放锁的时机:内置锁(监视器锁)
线程在进入同步代码块之前,会自动获取该锁,并且退出代码块时会自动释放该锁。无论是正常退出或者抛出异常退出,都会释放锁。
然而获取锁的唯一途径:进入这个锁保护的同步代码块或者同步方法中。
Jvm字节码:
1)将Java文件编程为 .class文件:javac xxx.java;
2)通过反编译查看字节码,javap -verbose xxx.class;
3)synchronized如何实现的,有个加锁monitorenter和解锁monitorexit读到该指令,会让monitor计数器+1或-1。
注意点:线程既可以在方法完成之后退出,也可以在抛出异常后退出,因此monitorexit数量多于monitorenter。
可重入原理:(加锁次数计数器)
1)jvm负责跟踪对象被加锁的次数。
2)线程第一次给对象加锁的时候,计数变为1.每当这个相同线程在此对象上再次获得锁时,计数会递增。
3)每当任务离开时,计数递减,当计数为0时,锁被完全释放。
(1)可重入:如果线程已拿到锁之后,还想再次进入由这把锁所控制的方法中,而无需提前释放,可以直接进入。
(2)可重入:指的是同一线程的外层函数获得锁之后,内层函数可以直接再次获取该锁。也叫做递归锁。Java中两大递归锁:Synchronized和ReentrantLock。
可见性原理:java内存模型
线程A向线程B发送数据的两个步骤:
1)线程A修改了本地内存A,并将其存储到主内存中。
2)线程B再从主内存中读取出来。
这个过程是由JMM(Java Memory Model)控制的。JMM通过控制主内存与每个线程的本地内存的交互来为Java程序员提供内存可见性的保证。
synchronized是如何做到内存可见性的实现?
一旦一个代码块或者方法被synchronized修饰之后,那么它在执行完毕之后被锁住的对象所做的任何修改都要在释放锁之前从线程内存写回到主内存 中。所以下一个线程从主内存中读取到的数据一定是最新的。就是通过这样的原理,synchronized关键字保证了每一次执行都是可靠的,保证了可见性。
9.Synchronized的缺陷
1)效率低:
锁的释放情况少;试图获得锁时不能设定超时;不能中断一个正在试图获得锁的线程。
2)不够灵活(读写锁更灵活:读操作的时候不会加锁,写操作的时候才会加锁):
加锁和释放的时机单一;每个锁仅有单一的条件(某个对象),可能是不够的。
3)无法知道是否成功获取到锁。
但是,lock有一些不一样的特性:
Lock可以尝试成功了做一些逻辑判断,如果没有成功做另外一些逻辑判断.
Lock类:
lock.lock();lock.unlock();
通过这两个方法,可以手动加锁和解锁。
lock.tryLock();lock.tryLock(10, TimeUnit.MINUTES);
可以判断是否加锁,返回类型为boolean
补充重点:
1.synchronized的使用注意点:
锁对象不能为空:锁的信息保存在对象头里面作用域不宜过大:synchronized关键字包裹的范围。
不需要串行工作的情况下,用并行的方式可以提高运行的效率避免死锁。
2.如何选择Lock和synchronized关键字?
1)如果可以的情况下,两者都不要选择,而是使用java.util.concurrent包中的各种各样的类,例如:CountDownLatch等。使用这些类,不需要自己做同步工作,更方便,也更不容易出错。
2)如果synchronized关键字在程序中适用,就优先实用这个关键字。因为这样可以减少需要编写的代码,就减少了出错的几率。
3)如果特别需要Lock这样结构独有的特性的时候,才使用Lock。
以上三点主要是基于减少代码出错为出发点。
10.思考题
1)多个线程等待同一个synchronized锁的时候,JVM如何选择下一个获取锁的是哪个线程?
锁调度机制。对于synchronized内置锁,不同版本的JVM处理方式不同,blocked和running都有几率。
2)synchronized使得同时只有一个线程可以执行,性能较差,有什么办法可以提升性能?
(1)优化使用范围,让加锁区在业务允许的情况下足够小。
(2)用其他类型的锁,例如读写锁,这样在读的时候就不止一个线程可以同时进入代码。
3)我想更灵活地控制锁的获取和释放(现在释放锁的时机都被规定死了),怎么办?
自己实现一个Lock
4)什么是锁的升级、降级?什么是JVM里的偏斜锁、轻量级锁、重量级锁?
在之前的JVM版本中,synchronized性能不是特别的好,而经过不断的迭代,synchronized性能已经得到了显著的提高,这里面运用的技术就是偏斜锁、轻量级锁、重量级锁。JVM会根据synchronized关键字使用到的次数或者其他的种种指标对锁进行有效的优化使得性能得到大幅上涨,这里面还涉及到了对象头里面的字段。
Java高并发syncronized深入理解的更多相关文章
- java高并发----个人学习理解汇总记录
		1.首先,需要理解几个概念 1.同步(Synchronous):同步方法调用一旦开始,调用者必须等到前面的方法调用返回后,才能继续后续的行为,依次直到完成所有. 2.异步(Asynchronous): ... 
- java高并发系列 - 第5天:深入理解进程和线程
		进程 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础.程序是指令.数据及其组织形式的描述,进程是程序的实体. 进程具有的 ... 
- 《实战java高并发程序设计》源码整理及读书笔记
		日常啰嗦 不要被标题吓到,虽然书籍是<实战java高并发程序设计>,但是这篇文章不会讲高并发.线程安全.锁啊这些比较恼人的知识点,甚至都不会谈相关的技术,只是写一写本人的一点读书感受,顺便 ... 
- Java高并发如何解决
		Java高并发如何解决 对于我们开发的网站,如果网站的访问量非常大的话,那么我们就需要考虑相关的并发访问问题了.而并发问题是绝大部分的程序员头疼的问题,但话又说回来了,既然逃避不掉,那我们就坦然面对吧 ... 
- Java高并发--AQS
		Java高并发--AQS 主要是学习慕课网实战视频<Java并发编程入门与高并发面试>的笔记 AQS是AbstractQueuedSynchronizer的简称,直译过来是抽象队列同步器. ... 
- Java高并发秒杀API之业务分析与DAO层
		根据慕课网上关于java高并发秒杀API的课程讲解用maven+ssm+redis实现的一个秒杀系统 参考了codingXiaxw's blog,很详细:http://codingxiaxw.cn/2 ... 
- 转载:Java高并发,如何解决,什么方式解决
		原文:https://www.cnblogs.com/lr393993507/p/5909804.html 对于我们开发的网站,如果网站的访问量非常大的话,那么我们就需要考虑相关的并发访问问题了.而并 ... 
- java高并发编程(三)
		java高并发主要有三块知识点: synchronizer:同步器,在多个线程之间互相之间怎么进行通讯,同步等: 同步容器:jdk提供了同步性的容器,比如concurrentMap,concurren ... 
- java高并发编程(一)
		读马士兵java高并发编程,引用他的代码,做个记录. 一.分析下面程序输出: /** * 分析一下这个程序的输出 * @author mashibing */ package yxxy.c_005; ... 
随机推荐
- 通过IP获取所在城市
			<script type="text/javascript"> var map = new BMap.Map("allmap"); var poin ... 
- Null 和 Undefined
			在JavaScript中存在这样两种原始类型:Null与Undefined.这两种类型常常会使JavaScript的开发人员产生疑惑,在什么时候是Null,什么时候又是Undefined? Undef ... 
- redhat server 5.3内核升极2.6.18 升级到 3.5 装systemtap  原创
			1. 在 LINUX 3.5源代码目录下执行 yum install ncurses-devel make menuconfig 2 打开内核跟踪事件,用于SYSTEMTAP跟踪 kern ... 
- java基础学习总结——对象转型
			一.对象转型介绍 对象转型分为两种:一种叫向上转型(父类对象的引用或者叫基类对象的引用指向子类对象,这就是向上转型),另一种叫向下转型.转型的意思是:如把float类型转成int类型,把double类 ... 
- 用最简单的例子理解策略模式(Strategy Pattern)
			当一个动作有多种实现方法,在实际使用时,需要根据不同情况选择某个方法执行动作,就可以考虑使用策略模式. 把动作抽象成接口,比如把玩球抽象成接口. public interface IBall { vo ... 
- 使用Lazy<T>实现对客户订单的延迟加载
			"延迟加载"是指在需要的时候再加载数据.比如获得一个Customer信息,并不会把该Customer的Orders信息一下加载出来,当需要显示Orders的时候再加载.简单来说,就 ... 
- codeforces 444 C. DZY Loves Colors(线段树)
			题目大意: 1 l r x操作 讲 [l,r]上的节点涂成x颜色,而且每一个节点的值都加上 |y-x| y为涂之前的颜色 2 l r 操作,求出[l,r]上的和. 思路分析: 假设一个区间为同样的颜 ... 
- 基于tomcat7 web开发中的一点小东西
			控制台: org.apache.jasper.compiler.TldLocationsCache tldScanJar 信息: At least one JAR was scanned for TL ... 
- Quartz Job执行后再更新Job的时间表达式....
			CronTrigger 表达式最大循环时间是1年?那么实际应用中的提醒超过1年怎么办呢? 先看一下界面: 参数说明:提前提醒时间:1-60分钟.1-24小时.1-90天:重复提醒:每1-59分钟.每1 ... 
- LaTeX快速入门-蔡炎龙
			蔡老师的这个文档只有26页,非常简短称得上是快速入门的文档了,TeX的基本入门这个文档还做不到,仅仅是一个简单的引子,让大家管中窥豹,先简单使用,然后才会更加深入进去. 文档早一个版本是用CJK排版的 ... 
