JAVA源码阅读笔记简版

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011648791/article/details/84202725

阅读顺序:

https://blog.csdn.net/qq_21033663/article/details/79571506

1、Object 

https://blog.csdn.net/chengzhang1989/article/details/78703818
1) wait(), notify(), notifyAll(), wait(timeout) 

对象监视器,ObjectMonitor

原来所谓的对象监视器就存储在对象的头部mark_word中!!!

锁正处在膨胀中,mark_word值为0x00000000时表示锁正在膨胀中

wait:

综上,我们来总结下Object.wait()实现原理:
a,首先能够调用wait方法,说明已经持有锁(或者说进入了synchronized代码块),而wait依赖于对象监视器objectmonitor的实现,所以首先得要获取这个monitor

b,monitor的指针存放在对象的头部,如果对象的头部没有,首先尝试从当前线程维护的omFreelist链表中分配,如果没有,从全局的gFreelist中分配,如果还没分配到,通过new的方式分配一个128大小的ObjectMonitor数组,放入到当前线程的omFreelist,以及全局的gFreelist,同时设置必要的信息

c,monitor分配好以后,进入wait,首先将当前线程封装成一个ObjectWaiter放入到objectMonitor的waitset队列中,然后退出对象监视器,挂起当前线程!
 

notify

notify时只是把notify标志职位1,当前线程加入到enter(synchronized)队列中,准备重新获取锁。既没有unpark挂起的线程,也没有调用ObjectMonitor::exit方法释放锁!!!!!

主要区别就是wait放入waitset然后会park;

notify是放入enter但是没有调用park

2) hashCode(), equals() 
3) clone()

深拷贝,浅拷贝

2、String 
1) char[] value 
2) int hash 
3) equals(), startWith(), endWith(), replace

3、AbstractStringBuilder 
1) char[] value 
2) int count 
3) 扩容:翻倍,内存不够则取所需最小。 将扩大的部分全部用’\0’

扩容或add前先确认长度,后变长

int newCapacity = (value.length + 1) * 2;扩容

char数组

append 先扩容

利用System.arraycopy

insert在某个位置插入str:System.arraycopy(value, index, value, index + len, count - index);

4、StringBuffer 
1) 继承AbstractStringBuilder 
2) synchronized方法保证线程安全 
3) char[] toStringCache

5、StringBuilder 继承AbstractStringBuilder

13.Thread

https://blog.csdn.net/caoxiaohong1005/article/details/80312396

JVM退出时Daemon线程中的finally块中的代码不一定会执行。因此不能依靠finally块中的内容来确保执行关闭或清理资源的逻

https://blog.csdn.net/linxdcn/article/details/72819817?locationNum=13&fps=1

四个状态

 BLOCKED 和 WATING 的区别, 一个是在临界点外面等待进入, 一个是在临界点里面wait等待别人notify, 

流程、主要方法、总结

start()

// 启动一个线程 public synchronized void start() { // 线程不能重复start if (threadStatus != 0)

interrupt()

同步锁,标志位

synchronized (blockerLock) { Interruptible b = blocker; if (b != null) { // 只是设置了中断标志位

join()

调用wai(0)

不用stop

https://www.cnblogs.com/DreamDrive/p/5623804.html

ThreadLocal

https://www.cnblogs.com/dolphin0520/p/3920407.html

ThreadLocal,很多地方叫做线程本地变量,也有些地方叫做线程本地存储,其实意思差不多。可能很多朋友都知道ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。

没有先set,直接get的话,运行时会报空指针异常。

  但是如果改成下面这段代码,即重写了initialValue方法:

创建线程:

java中创建线程的三种方法以及区别

Java使用Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例。Java可以用三种方式来创建线程,如下所示:

1)继承Thread类创建线程

2)实现Runnable接口创建线程

3)使用Callable和Future创建线程

Java通过Executors提供四种线程池,分别为:
newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

2、java.util

6、ArrayList 
1) Object[] elementData 
2) int size 
3) 默认大小10 
4) 扩容:翻倍,不够取所需最小

但ArrayList非线程安全,达到数组长度时每次扩大50%:用>>1的方式来加原来的长度

所以>>1等于乘以0.5

add时限保证容量,检查好容量后,add对象进去

https://www.cnblogs.com/woshimrf/p/arraylist.html

7、LinkedList 
1) Node {E item, Node prev, Node next} 
2) int size 
3) Node first 
4) Node last 
5) linkFirst(), linkLast(), linkBefore(), unLinkFirst(), unLinkLast(), unLink(), indexOf()

8、HashMap 

key可以为null


1) Node{int hash, K key, V value, Node next} 
2) 默认容量16,负载因子0.75f 
3) int size, modCount, threshold, float loadFactor 
4) Node[] table 
5) Set entrySet 
6) put():根据key算hash,根据容量和hash算index,table[index]没有直接添加到数组中,table[index]有,若index位置同一个key则更新,否则遍历next是否有,有则更新,无则新增,最后根据thread与size判断是否扩容。注:扩容时容量翻倍,重新算hash复制到新数组 
7)get()类似 
注:先比较hash,若相等在比较equals

9、Hashtable 
1) 结构实现与HashMap基本一致 
2)通过synchronized方法保证线程安全

10、HashSet:委托给HashMap,其Value是同一个默认对象

11、LinkedHashMap继承HashMap 
1) Entry{HashMap.Node, Entry before, after} 
2) Entry head, tail 
3) 重写newNode()添加节点时,除像HashMap中添加外,保存before、after信息

12、LinkedHashSet继承HashSet:不知道如何实现的顺序?

13、AbstractMap维护EntrySet,AbstractSet维护Iterator,AbstractList维护Iterator

14、ConcurrentHashMap 
1) JDK1.7及以前: 
a、Segment[] ,HashEntry[] , HashEntry{hash, k, v, next} 
b、根据key算hash,根据hash和Segment的大小算位置,每个segment拥有一个自己的HashEntry[] 
c、get():不加锁,volatile类型 
d、put(): 对相应segment加锁 
e、size():各HashEntry[] 之和,先不加锁算两遍,若一致则返回,若不一致则加锁重新计算 
2)JDK1.8 
a、Node{hash, key, value, next} 
b、Node[] table 
c、大多数操作类似于HashMap,不同CAS方式设置,根据key算hash,在根据hash和容量算index,对table[index]加锁,从而达到更大的并发量 
d、get(): 同HashMap 
e、put(): 对table[index]加锁

15、TreeMap 
1)红黑树,即自平衡二叉查找树,时间复杂度O(logn) 
2)Entry{K k, V v, Entry parent, left, right, boolean color} 
3)Entry root,int size, int modeCount

16、TreeSet:委托TreeMap实现

18) Collections 3

https://www.jianshu.com/p/51ce612db017

binarySearch方法

该方法会根据条件的不同调用两个私有的方法来进行查找。如果List支持随机访问,并且小于二分查找的阈值5000,则调用indexedBinarySearch,否则,调用iteratorBinarySearch,借助迭代器来进行访问。

shuffle方法

  shuffle翻译过来是重新洗牌的意思,该方法是将list原有数据打乱生成一个新的乱序列表。通俗点来说,旧相当于重新洗牌,打乱原来的顺序。还有一点,shuffle方法再生成乱序列表的时候,所有元素发生交换的可能性是近似相等的。

19) Arrays 3

26) SubList 4

7、java.util.concurrent.locks

1) Lock 2

公平锁和非公平锁通过new ReentrantLock(true or fals)决定

https://blog.csdn.net/future234/article/details/80576623

公平与非公平的lock方法区别:先尝试用乐观锁update来插队

1、非公平

//非公平锁if里面的条件就是插队,

//compareAndSetState(0, 1)

//意思就是插队看能不能拿到锁 //能拿到把当前线程设为sync的独占线程

否则就是正常acquire获得锁

2、公平

公平锁只能正常获得锁

获得锁流程:

1、公平

若锁是否空闲(打菜阿姨是否空闲)

hasQueuedPredecessors表示是否在队首

update乐观锁、设置当前线程

若当前线程重入锁返回true,状态加一

2、非公平

获得锁流程会再次尝试插队。

获取不到锁,就addWaiter放入队列:入队是循环,先创建队列,后入最后

acquireQueued循环获取锁:判断当前这个节点是不是头节点

释放锁:状态置位0,然后当前线程置位null

2) Condition 2

3) ReentrantLock 2

https://blog.csdn.net/mayongzhan_csdn/article/details/79374996

protected final boolean compareAndSetState(int expect, int update) {
        // See below for intrinsics setup to support this
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }

要点0的答案:AQS的volatile int state +1表示获取到了锁。unsafe返回true就是能获取

要点1的答案

a: 保证变量在线程之间的可见性 就是上面说的 
b:禁止指令重排序 在编译阶段插入内存屏障,来特定禁止指令重排序

update时如乐观锁volatile

重入机制:

在tryaquiry方法中

先判断状态0

后判断是否为当前线程

两个循环

一个循环入队:判断队头和创建队,第二个循环就入队

一个是循环唤醒:判断是否可用,当前的node是否已经在队头

4) ReentrantReadWriteLock 2

https://blog.csdn.net/mayongzhan_csdn/article/details/80731935

猜你喜欢

转载自blog.csdn.net/u011648791/article/details/84202725