标签:无法 super 加锁 情况 ring 简单 -name ctas 高效
说一下Spring中bean的加载过程,BeanFactory和FactoryBean有什么区别
Spring 作为 Ioc 框架,实现了依赖注入,由一个中心化的 Bean 工厂来负责各个 Bean 的实例化和依赖管理。各个 Bean 可以不需要关心各自的复杂的创建过程,达到了很好的解耦效果。
我们对 Spring 的工作流进行一个粗略的概括,主要为两大环节:
getBean
方法获取。BeanFactory与FactoryBean的区别
BeanFactory 以Factory结尾,表示它是一个工厂类,用于管理Bean的一个工厂
在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)来进行管理的。
但对FactoryBean而言,这个Bean不是简单的Bean,而是一个能生产或者修饰对象生成的工厂Bean,
它的实现与设计模式中的工厂模式和修饰器模式类似。
Synchronized的底层原理字节码层面如何实现加锁
synchronized 是解决Java并发最常见的一种方法,也是最简单的一种方法。关键字 synchronized 可以保证在同一时刻,只有一个线程可以访问某个方法或者某个代码块。同时 synchronized 也可以保证一个线程的变化,被另一个线程看到(保证了可见性)
这里要注意:synchronized是一个互斥的 重量级锁 (细节部分后续会讲)
synchronized的作用主要有三个:
在Java中所有对象都可以作为锁,这是synchronized实现同步的基础。
synchronized主要有三种应用方式:
Java虚拟机中的同步(Synchronization)都是基于进入和退出Monitor对象实现,无论是显示同步(同步代码块)还是隐式同步(同步方法)都是如此。
monitorenter
指令插入到同步代码块的开始位置。monitorexit
指令插入到同步代码块结束的位置。JVM需要保证每一个monitorenter
都有一个monitorexit
与之对应。虚拟机规范对 monitorenter 和 monitorexit 的行为描述中,有两点需要注意。首先 synchronized 同步快对于同一条线程来说是可重入的,也就是说,不会出现把自己锁死的问题。其次,同步快在已进入的线程执行完之前,会阻塞后面其他线程的进入。(摘自《深入理解JAVA虚拟机》)
要理解低层实现,就需要理解两个重要的概念 Monitor 和 Mark Word
synchronized用到的锁,是存储在对象头中的。(这也是Java所有对象都可以上锁的根本原因)
HotSpot虚拟机中,对象头包括两部分信息:
Mark Word(对象头)和 Klass Pointer(类型指针)
对象头的长度一般占用两个机器码(32位JVM中,一个机器码等于4个字节,也就是32bit),但如果对象是数组类型,则需要三个机器码(多出的一块记录数组长度)。
下图是对象头运行时的变化状态:
锁标志位 和 是否偏向锁 确定唯一的锁状态
其中 轻量锁 和 偏向锁 是JDK1.6之后新加的,用于对synchronized优化。稍后讲到
Monitor是 synchronized 重量级 锁的实现关键。锁的标识位为 10 。当然 synchronized作为一个重量锁是非常消耗性能的,所以在JDK1.6以后做了部分优化,接下来的部分是讲作为重量锁的实现。
Monitor是线程私有的数据结构,每一个对象都有一个monitor与之关联。每一个线程都有一个可用monitor record列表(当前线程中所有对象的monitor),同时还有一个全局可用列表(全局对象monitor)。每一个被锁住的对象,都会和一个monitor关联。
当一个monitor被某个线程持有后,它便处于锁定状态。此时,对象头中 MarkWord的 指向互斥量的指针,就是指向锁对象的monitor起始地址。
monitor是由 ObjectMonitor 实现的,其主要数据结构如下:(位于HotSpot虚拟机源码ObjectMonitor.hpp文件,C++实现的)
ObjectMonitor() {
_header = NULL;
_count = 0; //记录个数
_waiters = 0,
_recursions = 0;
_object = NULL;
_owner = NULL;
_WaitSet = NULL; //处于wait状态的线程,会被加入到_WaitSet
_WaitSetLock = 0 ;
_Responsible = NULL ;
_succ = NULL ;
_cxq = NULL ;
FreeNext = NULL ;
_EntryList = NULL ; //处于等待锁block状态的线程,会被加入到该列表
_SpinFreq = 0 ;
_SpinClock = 0 ;
OwnerIsThread = 0 ;
}
object monitor 有两个队列 _EntryList
和 _WaitSet
,用来保存ObjectWaiter对象列表(每个等待锁的线程都会被封装成ObjectWaiter对象)_owner
指向持有 objectMonitor的线程。
当多个线程同时访问一个同步代码时,首先会进入 _EntryList
集合,当线程获取到对象的monitor后,会进入_owner 区域,然后把monitor中的 _owner
变量修改为当前线程,同时monitor中的计数器_count
会加1。
根据虚拟机规范的要求,在执行monitorenter指令时,会尝试获取对象的锁。如果对象没有被锁定(获取锁),获取对象已经被该线程锁定(锁重入)。则把计数器加1(
_count
加1)。相应的,在执行monitorexit指令时,会讲计数器减1。当计数器为0时,_owner指向Null,锁就被释放。(摘自《深入理解JAVA虚拟机》)
如果线程调用 wait()
方法,将释放当前持有的monitor,_owner
变量恢复为null,_count
变量减1,同时该线程进入_WaitSet
等待被唤醒。
从Javac编译成的字节码可以看出(具体编译文件看参考链接),同步代码块使用的是monitorenter
和monitorexit
指令,其中monitorenter
指向同步代码块的开始位置,monitorexit
指向同步代码块的结束位置。
在线程执行到monitorenter
指令时,当前线程将尝试获取锁,即尝试获取锁对象对应的monitor的持有权。当monitor的count计数器为0,或者monitor的owner已经是该线程,则获取锁,count计数器+1。
如果其他线程已经持有该对象的锁,则该线程被阻塞,直到其他线程执行完毕释放锁。
线程执行完毕时,count归零,owner指向Null,锁释放。
值得注意的是,编译器将会确保,无论通过何种方法完成,方法中的每一条monitorenter
指令,最终都会有monitorexit
指令对应,不论这个方法正常结束还是异常结束,最终都会配对执行。
编译器会自动产生一个异常处理器,这个处理器声明可以处理所有的异常,它的目的就是为了确保monitorexit
指令最终执行。
方法级的同步是隐式,即无需通过字节码来控制的,它实现在方法调用和返回操作中。
在Class文件方法常量池中的方法表结构(method_info Structure)中, ACC_SYNCHRONIZED 访问标志区分一个方法是否为同步方法。在方法被调用时,会检查方法的 ACC_SYNCHRONIZED 标记是否被设置。如果被设置了,则线程将持有该方法对应对象的monitor(调用方法的实例对象or静态方法的类对象),然后再执行该方法。
最后在方法执行完成时,释放monitor。
在方法执行期间,执行线程持有了monitor,其他任何线程都无法再获得同一个monitor。
以下是字节码实现:
public class SyncMethod {
public int i;
public synchronized void syncTask(){
i++;
}
}
使用javap反编译后的字节码如下:
Classfile /Users/zejian/Downloads/Java8_Action/src/main/java/com/zejian/concurrencys/SyncMethod.class
Last modified 2017-6-2; size 308 bytes
MD5 checksum f34075a8c059ea65e4cc2fa610e0cd94
Compiled from "SyncMethod.java"
public class com.zejian.concurrencys.SyncMethod
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool;
//省略没必要的字节码
//==================syncTask方法======================
public synchronized void syncTask();
descriptor: ()V
//方法标识ACC_PUBLIC代表public修饰,ACC_SYNCHRONIZED指明该方法为同步方法
flags: ACC_PUBLIC, ACC_SYNCHRONIZED
Code:
stack=3, locals=1, args_size=1
0: aload_0
1: dup
2: getfield #2 // Field i:I
5: iconst_1
6: iadd
7: putfield #2 // Field i:I
10: return
LineNumberTable:
line 12: 0
line 13: 10
}
SourceFile: "SyncMethod.java"
从字节码可以看出,synchronized修饰的方法并没有monitorenter
和monitorexit
指令。而是用ACC_SYNCHRONIZED
的flag标记该方法是否是同步方法,从而执行相应的同步调用。
ReentrantLock如何实现非公平锁的“非公平锁”与“公平锁”区别
(1) synchronized 是Java的一个内置关键字,而ReentrantLock是Java的一个类。
(2) synchronized只能是非公平锁。而ReentrantLock可以实现公平锁和非公平锁两种。
(3) synchronized不能中断一个等待锁的线程,而Lock可以中断一个试图获取锁的线程。
(4) synchronized不能设置超时,而Lock可以设置超时。
(5) synchronized会自动释放锁,而ReentrantLock不会自动释放锁,必须手动释放,否则可能会导致死锁。
(1) 线程在获取锁调用lock()时,非公平锁首先会进行一次CAS尝试抢锁,如果此时没有线程持有锁或者正好此刻有线程执行完释放了锁(state == 0),那么如果CAS成功则直接占用锁返回。
(2) 如果非公平锁在上一步获取锁失败了,那么就会进入nonfairTryAcquire(int
acquires),在该方法里,如果state的值为0,表示当前没有线程占用锁或者刚好有线程释放了锁,那么就会CAS抢锁,如果抢成功了,就直接返回了,不管是不是有其他线程早就到了在阻塞队列中等待锁了。而公平锁在这里抢到锁了,会判断阻塞队列是不是空的,毕竟要公平就要讲先来后到,如果发现阻塞队列不为空,表示队列中早有其他线程在等待了,那么公平锁情况下线程会乖乖排到阻塞队列的末尾。
如果非公平锁 (1)(2) 都失败了,那么剩下的过程就和非公平锁一样了。
(3) 从(1)(2) 可以看出,非公平锁可能导致线程饥饿,但是非公平锁的效率要高。
concurrentHashmap是安全的吗,concurrentHashmap的size怎么求
并发场景下,需要怎么设定锁
mysql用什么索引
Mysql目前主要有以下几种索引类型:FULLTEXT,HASH,BTREE,RTREE。
1. FULLTEXT
即为全文索引,目前只有MyISAM引擎支持。其可以在CREATE TABLE ,ALTER TABLE ,CREATE INDEX 使用,不过目前只有 CHAR、VARCHAR ,TEXT 列上可以创建全文索引。
全文索引并不是和MyISAM一起诞生的,它的出现是为了解决WHERE name LIKE “%word%"这类针对文本的模糊查询效率较低的问题。
2. HASH
由于HASH的唯一(几乎100%的唯一)及类似键值对的形式,很适合作为索引。
HASH索引可以一次定位,不需要像树形索引那样逐层查找,因此具有极高的效率。但是,这种高效是有条件的,即只在“=”和“in”条件下高效,对于范围查询、排序及组合索引仍然效率不高。
3. BTREE
BTREE索引就是一种将索引值按一定的算法,存入一个树形的数据结构中(二叉树),每次查询都是从树的入口root开始,依次遍历node,获取leaf。这是MySQL里默认和最常用的索引类型。
4. RTREE
RTREE在MySQL很少使用,仅支持geometry数据类型,支持该类型的存储引擎只有MyISAM、BDb、InnoDb、NDb、Archive几种。
相对于BTREE,RTREE的优势在于范围查找。
B+树为什么索引快(B+树优点)
幻读是什么,Innodb如何避免幻读
间隙锁的锁定范围是多少
JVM内存模型
gc的种类 mingc和fullgc的区别,什么时候发生gc
新生代 GC(Minor GC):指发生在新生代的垃圾收集动作,因为 Java 对象大多都具
备朝生夕灭的特性,所以 Minor GC 非常频繁,一般回收速度也比较快。
老年代 GC(Major GC / Full GC):指发生在老年代的 GC,出现了 Major GC,经常
会伴随至少一次的 Minor GC(但非绝对的,在 ParallelScavenge 收集器的收集策略里
就有直接进行 Major GC 的策略选择过程) 。MajorGC 的速度一般会比 Minor GC 慢 10
倍以上。
Minor GC触发机制:
当年轻代满时就会触发Minor GC,这里的年轻代满指的是Eden代满,Survivor满不会引发GC。
Full GC触发机制:
(1)调用System.gc时,系统建议执行Full GC,但是不必然执行
(2)老年代空间不足
(3)方法区空间不足
(4)通过Minor GC后进入老年代的平均大小大于老年代的可用内存
(5)由Eden区、survivor space1(From Space)区向survivor space2(To Space)区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小
当永久代满时也会引发Full GC,会导致Class、Method元信息的卸载。
volatile底层实现
多核cpu在cpu级别怎么做到可见性
hashmap和concurrenthashmap的区别与实现
Hashmap本质是数组加链表。根据key取得hash值,然后计算出数组下标,如果多个key对应到同一个下标,就用链表串起来,新插入的在前面。
ConcurrentHashMap:在hashMap的基础上,ConcurrentHashMap将数据分为多个segment(段),默认16个(concurrency level),然后每次操作对一个segment(段)加锁,避免多线程锁的几率,提高并发效率。
HashMap不是线程安全的,而ConcurrentHashMap是线程安全的
string stringbuffer stringbuilder区别及实现
标签:无法 super 加锁 情况 ring 简单 -name ctas 高效
原文地址:https://www.cnblogs.com/ashin1997/p/11602979.html