面试题集合

JVM

01-什么是运行时数据区

1
2
javac XXX.java  ---->编译
java XXX ----?运行s

划分成一块块用来存储各种数据的地方
运行时数据区—》逻辑视图

堆—-》存储java对象
普通变量—-》随着对象存储在堆内存中
Method Area —-》存储类的信息 常量 静态变量 就是编译后的代码

堆和Method Area 被所偶线程所共享—》线程非安全的
一个线程—》创建一个java虚拟机栈
方法的执行可以通过压栈的方式表现—-》方法对应栈帧

native method –》本地方法栈 这地方没有私有的程序计数器

02-运行时数据区为什么要这么划分

image.png

03-方法的执行和Java虚拟机栈详解

class文件进行反编译 字节码指令 就能看到方法执行过程

javap -c Person.class

image.png

04-垃圾回收站和垃圾回收算法

垃圾回收

  • 运行时拘束—》内存模型设计
    内存模型一般堆方法区和堆这种共享的地方进行分析
    堆内用生命周期进行区域划分
    用类似队列的方式把新生代的对象转移到老年代
    young区再划分一个缓冲区,减少对象进入老年代的机会,因为老年代占用内存较大触发垃圾回收机制需要扫描的空间比新生代大

  • 解决缓冲区如果因为垃圾回收导致空间足够缺因为空间碎片无法存入数据的情况
    image.png
    解决方法将缓冲区一分为二
    一个空间永远放空,另外一个用来存放,循环交换。如下图
    此方法浪费将近百分之十的空间取得效果
    image.png
    image.png

  • 新对象创建
    image.png

什么样的对象才算是垃圾?

  1. 引用计数法
  2. 可达性分析 GC root

回收算法

  1. 标记-清除—用可达性分析
    image.png
    弊端:比较耗时,产生很多空间碎片,可能在有空间的情况下再次触发GC操作
  2. 标记-复制
    在1的基础上改进
    image.png
  3. 标记-整理
    image.png
    image.png

堆:Old,Young(Eden,s0,s1)
到底那个算法用在那个区域?
不同的代用不同的垃圾回收算法
Young整个区使用 复制算法但是又前提
image.png

Old区
一般存活时间比较长的,以为很难会被回收
所以使用标记整理或者标记清除算法

落地回收算法

垃圾收集器
常用垃圾收集器如下
image.png

G1垃圾收集器的过程
image.png

评价一个垃圾收集器的好坏

  1. 吞吐量 越高越好
  2. 停顿时间 越低越好
    分享一个GC在线分析网站

GCeasy
然后根据每个垃圾回收器的数据进行调优

image.png

减少GC的频率优化

  1. 适当的增加堆内存的空间
  2. 合理的设置G1垃圾收集器的停顿时间
  3. 垃圾回收的临界线

-XX:initiatingHeapOccupancPercent =50
4. 增加垃圾回收线程的数量
-XX:ConcGCThreads=10

G1的最佳实践

等待补充

Young Gc : Minor GC
Old GC :Major

Young+Old =====>Minor GC + Major GC ====>Full GC

插件 Visual Gc

等待补充

了解空间担保机制

Jvm学习过程

image.png

底层原理

HashMap和ConcurrentHash

hashmap
1.7—数组+链表
1.8—-数组+链表+红黑树
image.png

描述一下HashMap map.put过程

  1. 先有一个数据
    Node[] table = new Node[8];

  2. 根据key,vaule,创建一个Node
    new Node();

  3. image.png

hash算法的设计–Node节点下表的位置

image.png

如何降低Node节点下标位置重复率

  1. 第一个思路
    把hashcode和数组大小进行与运算
    为什么不用取摩运算,因为没有与运算效率高
    image.png

  2. 第二个思路,第一个改进(正确思路)
    原因,还是会重复下标
    image.png
    解决方法
    用高低十六位做一个异或运算
    因为用异或运算得到0和1的概率为50%而用与和或运算的概率分别为25和75

image.png

  1. 源码解析
    高低十六位异或运算

    1
    2
    3
    4
    5
    static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

  2. 一定要保证数据组大小一定是2的n次方
    原因
    image.png
    代码如下默认大小16
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
    如果定义的大小不是2的n次方会在HashMap的构造函数中运行如下代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    static final int tableSizeFor(int cap) {
    int n = cap - 1;
    n |= n >>> 1;
    n |= n >>> 2;
    n |= n >>> 4;
    n |= n >>> 8;
    n |= n >>> 16;
    return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
    }
  3. image.png
    首先判断数组是否为空,是的话初始化,不是的话获得数组
    然后判断数组下标位置是否为空,是的话放进去,不是的话就进行链表,以下是下标位置不为空的代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    else {
    Node<K,V> e; K k;
    if (p.hash == hash && //判断key值是否为空
    ((k = p.key) == key || (key != null && key.equals(k))))
    e = p;
    else if (p instanceof TreeNode)//红黑树存储
    e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
    else {//以链表的方式进行存储
    for (int binCount = 0; ; ++binCount) {
    if ((e = p.next) == null) {
    p.next = newNode(hash, key, value, null);
    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st TREEIFY_THRESHOLD=8判断是否超过链表长度
    treeifyBin(tab, hash); //单链表转红黑树
    break;
    }
    if (e.hash == hash &&
    ((k = e.key) == key || (key != null && key.equals(k))))
    break;
    p = e;
    }
    }
    if (e != null) { // existing mapping for key
    V oldValue = e.value;
    if (!onlyIfAbsent || oldValue == null)
    e.value = value;
    afterNodeAccess(e);
    return oldValue;
    }
    }

    代码不为空又分三种情况:

  • ((k = p.key) == key || (key != null && key.equals(k)))) key是否相同,是的话替换value
  • key不相同,以单链表方式进行存储,详情看上方代码
    • 链表长度超过默认8的时候自动从链表转化为红黑树,如果低于6,又会从红黑树转回链表
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
           /**
      * Replaces all linked nodes in bin at index for given hash unless
      * table is too small, in which case resizes instead.
      */
      final void treeifyBin(Node<K,V>[] tab, int hash) {
      int n, index; Node<K,V> e;
      if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY) //
      resize();
      else if ((e = tab[index = (n - 1) & hash]) != null) {
      TreeNode<K,V> hd = null, tl = null;
      do {
      TreeNode<K,V> p = replacementTreeNode(e, null);
      if (tl == null)
      hd = p;
      else {
      p.prev = tl;
      tl.next = p;
      }
      tl = p;
      } while ((e = e.next) != null);
      if ((tab[index] = hd) != null)
      hd.treeify(tab);
      }
      }
    • 红黑树存储代码
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      /**
      * Tree version of putVal.
      */
      final TreeNode<K,V> putTreeVal(HashMap<K,V> map, Node<K,V>[] tab,
      int h, K k, V v) {
      Class<?> kc = null;
      boolean searched = false;
      TreeNode<K,V> root = (parent != null) ? root() : this;
      for (TreeNode<K,V> p = root;;) {
      int dir, ph; K pk;
      if ((ph = p.hash) > h)
      dir = -1;
      else if (ph < h)
      dir = 1;
      else if ((pk = p.key) == k || (k != null && k.equals(pk)))
      return p;
      else if ((kc == null &&
      (kc = comparableClassFor(k)) == null) ||
      (dir = compareComparables(kc, k, pk)) == 0) {
      if (!searched) {
      TreeNode<K,V> q, ch;
      searched = true;
      if (((ch = p.left) != null &&
      (q = ch.find(h, k, kc)) != null) ||
      ((ch = p.right) != null &&
      (q = ch.find(h, k, kc)) != null))
      return q;
      }
      dir = tieBreakOrder(k, pk);
      }

      TreeNode<K,V> xp = p;
      if ((p = (dir <= 0) ? p.left : p.right) == null) {
      Node<K,V> xpn = xp.next;
      TreeNode<K,V> x = map.newTreeNode(h, k, v, xpn);
      if (dir <= 0)
      xp.left = x;
      else
      xp.right = x;
      xp.next = x;
      x.parent = x.prev = xp;
      if (xpn != null)
      ((TreeNode<K,V>)xpn).prev = x;
      moveRootToFront(tab, balanceInsertion(root, x));
      return null;
      }
      }
      }

数组大小的限制

如果数组索引不够用了,链表长度变成了
堆数组的大小进行扩容,
16 32 64 128 一定要是2的n次方

  1. 扩容的条件
    当Node节点的数量超过一定的值
    标准:数组大小*0.75
    1
    2
    3
    4
    5
    6
    7
    8
    /**
    * The load factor used when none specified in constructor.
    */
    static final float DEFAULT_LOAD_FACTOR = 0.75f;

    if (++size > threshold)
    resize(); //初始化和扩容数组

数组初始化和扩容代码

数组的迁移

  1. 先遍历老数组的索引位置,找出索引位置不为空的Node
  2. 对于不为空的Node索引位置进行判断
    • 下面没有Node next=null
    • 下面有Node,但是链表方式,需要遍历整个链表,因为会一分为二
    • 下面有Node,红黑树的方式
  3. 老数组迁移到新数组的位置
    image.png
    image.png
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85

/**
* Initializes or doubles table size. If null, allocates in
* accord with initial capacity target held in field threshold.
* Otherwise, because we are using power-of-two expansion, the
* elements from each bin must either stay at same index, or move
* with a power of two offset in the new table.
*
* @return the table
*/
final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table;
int oldCap = (oldTab == null) ? 0 : oldTab.length;
int oldThr = threshold;
int newCap, newThr = 0;
if (oldCap > 0) {
if (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return oldTab;
}
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold 翻倍容量
}
else if (oldThr > 0) // initial capacity was placed in threshold
newCap = oldThr;
else { // zero initial threshold signifies using defaults 扩大扩容数值
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
if (newThr == 0) {
float ft = (float)newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
threshold = newThr;
@SuppressWarnings({"rawtypes","unchecked"})
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap]; //得到一个新的数组
table = newTab;
if (oldTab != null) { //数组的迁移
for (int j = 0; j < oldCap; ++j) {
Node<K,V> e;
if ((e = oldTab[j]) != null) {
oldTab[j] = null;
if (e.next == null) //下面没有Node
newTab[e.hash & (newCap - 1)] = e;
else if (e instanceof TreeNode)
((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
else { // preserve order
Node<K,V> loHead = null, loTail = null;
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> next;
do {
next = e.next;
if ((e.hash & oldCap) == 0) {
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}
else {
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
if (loTail != null) { //原来的位置
loTail.next = null;
newTab[j] = loHead;
}
if (hiTail != null) { //新的位置
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;
}


ConcurrentHashMap(put的线程安全问题 )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* Initializes table, using the size recorded in sizeCtl.
*/
private final Node<K,V>[] initTable() {
Node<K,V>[] tab; int sc;
while ((tab = table) == null || tab.length == 0) {
if ((sc = sizeCtl) < 0)//有线程在跑的时候进行等待
Thread.yield(); // lost initialization race; just spin
else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) { //线程进来的时候标记SIZECTL
try {
if ((tab = table) == null || tab.length == 0) {
int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
@SuppressWarnings("unchecked")
Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
table = tab = nt;
sc = n - (n >>> 2);
}
} finally {
sizeCtl = sc;
}
break;
}
}
return tab;
}

  1. 初始化数组大小–线程非安全
    比如 T1—->16 T2—->32
    线程安全的保障
    CAS无锁化的机制保证线程的安全性 乐观锁

  2. put操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
/** Implementation for put and putIfAbsent */
final V putVal(K key, V value, boolean onlyIfAbsent) {
if (key == null || value == null) throw new NullPointerException();
int hash = spread(key.hashCode());
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0)
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
if (casTabAt(tab, i, null, //数组下标位置为空的时候 CAS
new Node<K,V>(hash, key, value, null)))
break; // no lock when adding to empty bin
}
else if ((fh = f.hash) == MOVED) // 判断是否有线程在进行迁移操作
tab = helpTransfer(tab, f); //帮助其他线程进行数组迁移
else {
V oldVal = null;
synchronized (f) { //对每个下标位置进行资源锁,每个Node互不干扰
if (tabAt(tab, i) == f) { //链表存储
if (fh >= 0) {
binCount = 1;
for (Node<K,V> e = f;; ++binCount) {
K ek;
if (e.hash == hash &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
oldVal = e.val;
if (!onlyIfAbsent)
e.val = value;
break;
}
Node<K,V> pred = e;
if ((e = e.next) == null) {
pred.next = new Node<K,V>(hash, key,
value, null);
break;
}
}
}
else if (f instanceof TreeBin) { //红黑树存储
Node<K,V> p;
binCount = 2;
if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
value)) != null) {
oldVal = p.val;
if (!onlyIfAbsent)
p.val = value;
}
}
}
}
if (binCount != 0) {
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
if (oldVal != null)
return oldVal;
break;
}
}
}
addCount(1L, binCount);
return null;
}

synchronized ??? NO 因为会对整个数组上锁

  • 数组下标位置为空的时候 CAS
  • 数组下标不为空的时候 put CAS机制不好
每个数组下标位置的锁就用synchronized
  1. 扩容存在的线程安全问题
    image.png
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    /**
    * Moves and/or copies the nodes in each bin to new table. See
    * above for explanation.
    */
    private final void transfer(Node<K,V>[] tab, Node<K,V>[] nextTab) {
    int n = tab.length, stride;
    if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE)
    stride = MIN_TRANSFER_STRIDE; // subdivide range 领取一个16单位的迁移
    if (nextTab == null) { // initiating 判断是否创建数组
    try {
    @SuppressWarnings("unchecked")
    Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n << 1];
    nextTab = nt;
    } catch (Throwable ex) { // try to cope with OOME
    sizeCtl = Integer.MAX_VALUE;
    return;
    }
    nextTable = nextTab;
    transferIndex = n;
    }
    int nextn = nextTab.length;
    ForwardingNode<K,V> fwd = new ForwardingNode<K,V>(nextTab);
    boolean advance = true;
    boolean finishing = false; // to ensure sweep before committing nextTab 判断每个数组迁移是否完成,并且是否全部领取完
    for (int i = 0, bound = 0;;) {
    Node<K,V> f; int fh;
    while (advance) {
    int nextIndex, nextBound;
    if (--i >= bound || finishing)
    advance = false;
    else if ((nextIndex = transferIndex) <= 0) {
    i = -1;
    advance = false;
    }
    else if (U.compareAndSwapInt
    (this, TRANSFERINDEX, nextIndex,
    nextBound = (nextIndex > stride ?
    nextIndex - stride : 0))) {
    bound = nextBound;
    i = nextIndex - 1;
    advance = false;
    }
    }
    if (i < 0 || i >= n || i + n >= nextn) {
    int sc;
    if (finishing) {
    nextTable = null;
    table = nextTab;
    sizeCtl = (n << 1) - (n >>> 1);
    return;
    }
    if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, sc - 1)) {
    if ((sc - 2) != resizeStamp(n) << RESIZE_STAMP_SHIFT)
    return;
    finishing = advance = true;
    i = n; // recheck before commit
    }
    }
    else if ((f = tabAt(tab, i)) == null)
    advance = casTabAt(tab, i, null, fwd);
    else if ((fh = f.hash) == MOVED)
    advance = true; // already processed
    else {
    synchronized (f) { //数据的迁移
    if (tabAt(tab, i) == f) {
    Node<K,V> ln, hn;
    if (fh >= 0) {
    int runBit = fh & n;
    Node<K,V> lastRun = f;
    for (Node<K,V> p = f.next; p != null; p = p.next) {
    int b = p.hash & n;
    if (b != runBit) {
    runBit = b;
    lastRun = p;
    }
    }
    if (runBit == 0) {
    ln = lastRun;
    hn = null;
    }
    else {
    hn = lastRun;
    ln = null;
    }
    for (Node<K,V> p = f; p != lastRun; p = p.next) {
    int ph = p.hash; K pk = p.key; V pv = p.val;
    if ((ph & n) == 0)
    ln = new Node<K,V>(ph, pk, pv, ln);
    else
    hn = new Node<K,V>(ph, pk, pv, hn);
    }
    setTabAt(nextTab, i, ln);
    setTabAt(nextTab, i + n, hn);
    setTabAt(tab, i, fwd);
    advance = true;
    }
    else if (f instanceof TreeBin) {
    TreeBin<K,V> t = (TreeBin<K,V>)f;
    TreeNode<K,V> lo = null, loTail = null;
    TreeNode<K,V> hi = null, hiTail = null;
    int lc = 0, hc = 0;
    for (Node<K,V> e = t.first; e != null; e = e.next) {
    int h = e.hash;
    TreeNode<K,V> p = new TreeNode<K,V>
    (h, e.key, e.val, null, null);
    if ((h & n) == 0) {
    if ((p.prev = loTail) == null)
    lo = p;
    else
    loTail.next = p;
    loTail = p;
    ++lc;
    }
    else {
    if ((p.prev = hiTail) == null)
    hi = p;
    else
    hiTail.next = p;
    hiTail = p;
    ++hc;
    }
    }
    ln = (lc <= UNTREEIFY_THRESHOLD) ? untreeify(lo) :
    (hc != 0) ? new TreeBin<K,V>(lo) : t;
    hn = (hc <= UNTREEIFY_THRESHOLD) ? untreeify(hi) :
    (lc != 0) ? new TreeBin<K,V>(hi) : t;
    setTabAt(nextTab, i, ln);
    setTabAt(nextTab, i + n, hn);
    setTabAt(tab, i, fwd);
    advance = true;
    }
    }
    }
    }
    }
    }


    解决
    任何一个线程进行迁移的操作,不管你是第一个线程还是第几个线程
    第一个线程负责创建新的数组
    所有的线程领取格子的任务并且以16大小的单位的领取
    private static final int MIN_TRANSFER_STRIDE = 16;

扩容到64位后进行红黑树判断

高并发分布式开发

如何保证redis和数据库的一致性

为什么用redis nosql
提高并发量 ,key/value形式的存储

  1. 先更新数据库再更新缓存
  2. 先删除缓存再更新数据库
  3. 先更新数据库后删除缓存选择比较多,脏数据出现比较少
    image.png

Redis里面的LRU机制

Least recently Used
淘汰机制
使用Redis Object

  1. 随机选择5格key把idele time最大的key移除 maxmeory-samples 5 每次随即后的选择结果没有记录
  2. 引入一个缓冲池pool
  3. redis用到了一种近似随机的算法
  4. maxmemory_policy 缓存淘汰策略
    image.png
  5. LRU会带来内存碎片,如何解决 共同的方案 借鉴JVM的内存回收解决内存碎片的问题

什么是zookeeper以及Zookeeper的应用

分布式开源协调服务
google chubby 是不开源的 zookeeper是google chubby的开源实现
解决分布式一致性问题
解决分布式锁服务

  1. 基于dubbo+zk来实现服务地址的注册和监听
    image.png
  2. 配置中心
    image.png
  3. 分布式锁服务

synchronzied和lock什么区别

都是锁,解决线程安全问题。
1、Synchronized和Lock的用法和区别

1.synchronized是托管给JVM执行的,而Lock是Java写的控制锁的代码。
2.synchronized原始采用的是CPU悲观锁机制,即线程获得的是独占锁。独占锁意味着其他线程只能依靠阻塞来等待线程释放锁。而在CPU转换线程阻塞时会引起线程上下文切换,当有很多线程竞争锁的时候,会引起CPU频繁的上下文切换导致效率很低。 
3.Lock用的是乐观锁方式。每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。
4.ReentrantLock必须在finally中释放锁,否则后果很严重,编码角度来说使用synchronized更加简单,不容易遗漏或者出错。
5.ReentrantLock提供了可轮询的锁请求,他可以尝试的去取得锁,如果取得成功则继续处理,取得不成功,可以等下次运行的时候处理,所以不容易产生死锁,而synchronized则一旦进入锁请求要么成功,要么一直阻塞,所以更容易产生死锁。
6.synchronized的话,锁的范围是整个方法或synchronized块部分;而Lock因为是方法调用,可以跨方法,灵活性更大。

2、Synchronized和Lock的用法和区别

synchronized 是Java的关键字,是Java的内置特性,在JVM层面实现了对临界资源的同步互斥访问,但 synchronized 粒度有些大,在处理实际问题时存在诸多局限性,比如响应中断等。Lock 提供了比 synchronized更广泛的锁操作,它能以更优雅的方式处理线程同步问题。

在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。

性能比较:
在JDK1.5中,synchronized是性能低效的。因为这是一个重量级操作,它对性能最大的影响是阻塞的是实现,挂起线程和恢复线程的操作都需要转入内核态中完成,这些操作给系统的并发性带来了很大的压力。相比之下使用Java提供的Lock对象,性能更高一些。Brian Goetz对这两种锁在JDK1.5、单核处理器及双Xeon处理器环境下做了一组吞吐量对比的实验,发现多线程环境下,synchronized的吞吐量下降的非常严重,而ReentrankLock则能基本保持在同一个比较稳定的水平上。但与其说ReetrantLock性能好,倒不如说synchronized还有非常大的优化余地,于是到了JDK1.6,发生了变化,对synchronize加入了很多优化措施,有自适应自旋,锁消除,锁粗化,轻量级锁,偏向锁等等。导致在JDK1.6上synchronize的性能并不比Lock差。官方也表示,他们也更支持synchronize,在未来的版本中还有优化余地,所以还是提倡在synchronized能实现需求的情况下,优先考虑使用synchronized来进行同步。
当需要以下高级特性时,才应该使用Lock:可定时的、可轮询的与可中断的锁获取操作,公平队列,或者非块结构的锁。否则,请使用synchronized。

3、Synchronized和Lock的用法和区别

那么lock和synchronized的区别对比如下:
1)synchronized 在成功完成功能或者抛出异常时,虚拟机会自动释放线程占有的锁;而Lock对象在发生异常时,如果没有主动调用unLock()方法去释放锁,则锁对象会一直持有,因此使用Lock时需要在finally块中释放锁;
2)lock接口锁可以通过多种方法来尝试获取锁包括立即返回是否成功的tryLock(),以及一直尝试获取的lock()方法和尝试等待指定时间长度获取的方法,相对灵活了许多比synchronized;
3) 通过在读多,写少的高并发情况下,我们用ReentrantReadWriteLock分别获取读锁和写锁来提高系统的性能,因为读锁是共享锁,即可以同时有多个线程读取共享资源,而写锁则保证了对共享资源的修改只能是单线程的。

Spring Cloud的理解

基础框架

JAVA反射机制

java创建类的方式有四种

  1. new
  2. 反射
  3. 反序列化
  4. 克隆
    class对象获取的方式方法有哪些?
    类型.class
    getclass
    forname
    ClassLoader.loadClass(“全路径”)
    image.png
    image.png
    反射主要是通过包名来创建对象,反射会增加系统损耗,安全管理机制

类的加载过程

image.png
image.png
image.png
image.png

JDBC变成为什么用class.forname?
因为这个会完成static语句 完成注册过程
而用ClassLoader.load不会完成static语句 连链接都没做更不会初始化

为什么clazz.newInstance()会抛出异常?
检查类是否有构造函数并且没有声明无参构造函数

Spring IOC容器和AOP和DI

image.png
image.png
image.png
image.png
image.png

  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!

扫一扫,分享到微信

微信分享二维码

请我喝杯咖啡吧~

支付宝
微信