1.Synchronized与ReentrantLock的区别,使用场景
Synchronized与ReentrantLock都是Java里面的锁,都可以实现多线程同步.
他们之间区别有:
- 实现不同
Synchronized关键字是JVM内部使用管程(Monitor)支持的。(什么是管程参考《Monitor(管程)是什么意思?Java中Monitor(管程)的介绍》)
synchronized实现同步的基础:Java中每个对象都可以作为锁。当线程试图访问同步代码时,必须先获得对象锁,退出或抛出异常时必须释放锁。Synchronzied实现同步的表现形式分为:代码块同步和方法同步。
JVM基于进入和退出Monitor对象来实现代码块同步和方法同步,两者实现细节不同。
代码块同步:在编译后通过将monitorenter指令插入到同步代码块的开始处,将monitorexit指令插入到方法结束处和异常处,通过反编译字节码可以观察到。任何一个对象都有一个monitor与之关联,线程执行monitorenter指令时,会尝试获取对象对应的monitor的所有权,即尝试获得对象的锁。
方法同步:synchronized方法在method_info结构有ACC_synchronized标记,线程执行时会识别该标记,获取对应的锁,实现方法同步。
两者虽然实现细节不同,但本质上都是对一个对象的监视器(monitor)的获取。任意一个对象都拥有自己的监视器,当同步代码块或同步方法时,执行方法的线程必须先获得该对象的监视器才能进入同步块或同步方法,没有获取到监视器的线程将会被阻塞,并进入同步队列,状态变为BLOCKED。当成功获取监视器的线程释放了锁后,会唤醒阻塞在同步队列的线程,使其重新尝试对监视器的获取。
ReentrantLock实现基于 CAS操作(CompareAndSwap)。CAS操作简单的说就是比较并交换。CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。无论哪种情况,它都会在 CAS 指令之前返回该位置的值。CAS 有效地说明了“我认为位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。” Java并发包(java.util.concurrent)中大量使用了CAS操作,涉及到并发的地方都调用了sun.misc.Unsafe类方法进行CAS操作。
- 使用方式不同
Synchronized使用在方法上或者代码块上
Synchronized的使用比较方便简洁,并且由编译器去保证锁的加锁和释放,而ReenTrantLock需要手工声明来加锁和释放锁,为了避免忘记手工释放锁造成死锁,所以最好在finally中声明释放锁。
- 功能不同
1.等待可中断,持有锁的线程长期不释放的时候,正在等待的线程可以选择放弃等待,这相当于Synchronized来说可以避免出现死锁的情况。通过lock.lockInterruptibly()来实现这个机制。
2.公平锁,多个线程等待同一个锁时,必须按照申请锁的时间顺序获得锁,Synchronized锁非公平锁,ReentrantLock默认的构造函数是创建的非公平锁,可以通过参数true设为公平锁,但公平锁表现的性能不是很好。
3.锁绑定多个条件,一个ReentrantLock对象可以同时绑定对个对象。ReenTrantLock提供了一个Condition(条件)类,用来实现分组唤醒需要唤醒的线程们,而不是像synchronized要么随机唤醒一个线程要么唤醒全部线程。
4 .都是可重入锁
- 性能区别
在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时ReentrantLock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。
在JDK1.5中,synchronized是性能低效的。因为这是一个重量级操作,它对性能最大的影响是阻塞的是实现,挂起线程和恢复线程的操作都需要转入内核态中完成,这些操作给系统的并发性带来了很大的压力。相比之下使用Java提供的ReentrankLock对象,性能更高一些。到了JDK1.6,发生了变化,对synchronize加入了很多优化措施,有自适应自旋,锁消除,锁粗化,轻量级锁,偏向锁等等。导致在JDK1.6上synchronize的性能并不比Lock差。官方也表示,他们也更支持synchronize,在未来的版本中还有优化余地,所以还是提倡在synchronized能实现需求的情况下,优先考虑使用synchronized来进行同步。
关于synchronized锁的优化参考《》
参考:
《ReentrantLock实现原理》
《Synchronized与ReentrantLock区别总结》
《轻松学习java可重入锁(ReentrantLock)的实现原理》
《关于synchronized和ReentrantLock之多线程同步详解》
2JVM自动内存管理,MinorGC与FullGC的触发机制
java技术体系中所倡导的的自动内存管理最终可以归纳为自动化的解决两个问题:给对象分配内存以及回收对象内存。
1)对象优先分配在Eden分配
大多数情况下,对象在新生代Eden区分配,当Eden区没有足够的空间进行分配时,虚拟机将发起一次 Monitor GC。
2)大对象直接进入老年代
所谓的大对象是指,需要大量的连续内存的空间的java对象,最典型的大对象就是那种很长的字符串以及数组,大对象对虚拟机来时是一个坏消息,比遇到一个大对象更坏的消息就是遇到一群 朝生夕死的”短命大对象“,经常出现大对象容易导致内存还有不少空间的时候就提前触发垃圾收集以获取足够的空间来安置他们。
3)长期存活的对象将进入老年代
虚拟机为每一个对象定义了一个对象年龄计算器,如果对象在Eden出生并经过第一次Minor GC后仍然存活,并且能被Survivor容纳的话,将被移到Survivor空间中,并且对象的年龄添加一岁,当他的年龄增加到一定的程度(默认是15岁),就会晋升到老年代,对象晋升到老年代的阀值可以通过 -XX:MaxTenuringThreshold设置
4)动态对象年龄判定
为了能更好的适应不同程序的内存状况,虚拟机并不是永远要求对象年龄达到了MaxTenuringThreshold 才进入老年代,如果在Survivor空间中相同年龄所有对象大小的综合大于Survivor空间的一半,年龄大于或者等于该年龄的对象就可以直接进入老年代,无需等到MaxTenuringThreshold的阀值年龄要求。
5)空间分配担保
在Minor GC之前,虚拟机会检查老年代最大的可用连续空间是否大于新生代所有对象总空间,如果是,那么Monitor GC确认是安全的,如果不是,虚拟机会查看HandleProotionFailure设置是否允许担保失败。如果允许,那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试执行一次GC,尽管这次GC是有分风险的,如果小于,或者HandleProotionFailure设置不允许冒险,那这时也要改为进行一次Full GC。
3 JVM调优基本思路
《常用的JVM调优参数总结汇总【随时查阅学习】》
《根据应用程序设置JVM参数(一)-设置堆、新生代、老年代、持久代大小》
将java堆的初始值-Xms和最大值-Xmx设置为老年代活跃数据大小的3~4倍
永久带的初始值-XX:PermSize及最大值-XX:MaxPermSize应该比永久代活跃数据大1.2~1.5倍
新生代空间应该为老年代空间活跃数据的1~1.5倍
如果java堆的初始值及最大值为活跃数据的3~4倍,新生代为活跃数据的1~1.5倍时,老年代应设置为活跃数据大小的2~3倍
4.如何设计海量数据的存储系统
一是数据分布算法将多条数据分散存储到多个存储节点,
二是存储引擎,
三是数据一致性协议 并发访问时或故障时实现数据多副本的一致性。
四是数据迁移,
五是磁盘管理,
六是数据容灾、恢复
分布式数据方案提供功能如下:
(1)提供分库规则和路由规则(RouteRule简称RR),将上面的说明中提到的三中切分规则直接内嵌入本系统,具体的嵌入方式在接下来的内容中进行详细的说明和论述;
(2)引入集群(Group)的概念,解决容错性的问题,保证数据的高可用性;
(3)引入负载均衡策略(LoadBalancePolicy简称LB);
(4)引入集群节点可用性探测机制,对单点机器的可用性进行定时的侦测,以保证LB策略的正确实施,以确保系统的高度稳定性;
(5)引入读/写分离,提高数据的查询速度;
《朱建平:如何架构海量存储系统》
《Mysql海量数据存储和解决方案之—分布式DB方案》
5.缓存的原理是什么?设计缓存要注意什么?
-
将数据写入/读取速度更快的存储(设备);
-
将数据缓存到离应用最近的位置;
-
将数据缓存到离用户最近的位置。
-
热点数据缓存、缓存穿透,缓存一致性
《缓存技术的详解》
《页面优化缓存技术+资源静态化+前后端分离?》
6.volatile关键字如何保证内存可见性
当一个变量定义为 volatile 之后,将具备两种特性:
1.保证此变量对所有的线程的可见性,这里的“可见性”,如本文开头所述,当一个线程修改了这个变量的值,volatile 保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新。但普通变量做不到这点,普通变量的值在线程间传递均需要通过主内存(详见:Java内存模型)来完成。
2.禁止指令重排序优化。有volatile修饰的变量,赋值后多执行了一个“load addl $0x0, (%esp)”操作,这个操作相当于一个内存屏障(指令重排序时不能把后面的指令重排序到内存屏障之前的位置),只有一个CPU访问内存时,并不需要内存屏障;(什么是指令重排序:是指CPU采用了允许将多条指令不按程序规定的顺序分开发送给各相应电路单元处理)。
7.happen-before原则
8. Lucene 全文搜索的原理
《lucene原理及java实现》
《剖析Lucene底层原理及基于他开发搜索引擎网站》