包括之前的 HashMapLinkedList, 都是基于 JDK 7的(应该全面拥抱JDK 8了)。

ArrayList 的 DOC 注释

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
/**
* Resizable-array implementation of the <tt>List</tt> interface. Implements
* all optional list operations, and permits all elements, including
* <tt>null</tt>. In addition to implementing the <tt>List</tt> interface,
* this class provides methods to manipulate the size of the array that is
* used internally to store the list. (This class is roughly equivalent to
* <tt>Vector</tt>, except that it is unsynchronized.)
* (ArrayList) 通过 List 接口实现的容量可变数组。实现了 list 的所有可选操作(方法),并且允许添加所有元素,包括 null。除了实现了 List 的接口,ArrayList 还提供了修改用来容纳 list 的数组的 size 的方法(ArrayList 是非线程安全的,除此以外大致上与 Vector 相同)。
* <p>The <tt>size</tt>, <tt>isEmpty</tt>, <tt>get</tt>, <tt>set</tt>,
* <tt>iterator</tt>, and <tt>listIterator</tt> operations run in constant
* time. The <tt>add</tt> operation runs in <i>amortized constant time</i>,
* that is, adding n elements requires O(n) time. All of the other operations
* run in linear time (roughly speaking). The constant factor is low compared
* to that for the <tt>LinkedList</tt> implementation.
* size,isEmpty,get,set,iterator 方法和 listIterator 操作的时间复杂度是O(1)的(常数级)。add() 方法的时间复杂度是O(1)+的(amortized constant time)(因为涉及到list 扩容,扩容也需要时间,需要把这部分时间平均分摊到每次 add 操作中。),也就是说,添加元素的时间复杂度为O(n)。其他操作的时间复杂度大致为O(n),常量值 n 通常要比 LinkedList 的时间复杂度中常量值要小(即,其他操作要比 LinkedList 的其他操作要快一点)。
* <p>Each <tt>ArrayList</tt> instance has a <i>capacity</i>. The capacity is
* the size of the array used to store the elements in the list. It is always
* at least as large as the list size. As elements are added to an ArrayList,
* its capacity grows automatically. The details of the growth policy are not
* specified beyond the fact that adding an element has constant amortized
* time cost.
* 每一个 ArrayList 实例都有一个 capacity(容量)属性,该属性不小于 list 的 size。当向 ArrayList 中添加元素时,它的 capacity 会自动增长。除了添加一个元素具有固定的摊余时间成本之外,增长规则的细节没有被指定。
* <p>An application can increase the capacity of an <tt>ArrayList</tt> instance
* before adding a large number of elements using the <tt>ensureCapacity</tt>
* operation. This may reduce the amount of incremental reallocation.
* 在程序中添加大量元素之前,可以调用 ensureCapacity() 方法来对 ArrayList 进行扩容。这样可以减少多次扩容所花费的时间(可以减少扩容的次数)
* <p><strong>Note that this implementation is not synchronized.</strong>
* If multiple threads access an <tt>ArrayList</tt> instance concurrently,
* and at least one of the threads modifies the list structurally, it
* <i>must</i> be synchronized externally. (A structural modification is
* any operation that adds or deletes one or more elements, or explicitly
* resizes the backing array; merely setting the value of an element is not
* a structural modification.) This is typically accomplished by
* synchronizing on some object that naturally encapsulates the list.
* 需要注意的是,ArrayList 对 List 的实现是非同步的。如果多线程同时请求对 ArrayList 的访问,并且至少会有一个线程对 ArrayList 进行结构性修改(结构性修改是指添加或删除一个以上的元素,或者显式的修改了其数组的大小。仅仅修改元素值并非结构性修改),那么务必在 ArrayList 外部进行 synchronized 修饰。这通常是通过 synchronized 修饰 封装了 list 的某个对象来实现的。
* If no such object exists, the list should be "wrapped" using the
* {@link Collections#synchronizedList Collections.synchronizedList}
* method. This is best done at creation time, to prevent accidental
* unsynchronized access to the list:<pre>
* List list = Collections.synchronizedList(new ArrayList(...));</pre>
* 如果(手头儿)没有这样的对象,那么应该用 Collections.synchronizedList 包裹 list。该操作最好在创建 list 时进行,以防意外的非同步访问 list。
* <p><a name="fail-fast"/>
* The iterators returned by this class's {@link #iterator() iterator} and
* {@link #listIterator(int) listIterator} methods are <em>fail-fast</em>:
* if the list is structurally modified at any time after the iterator is
* created, in any way except through the iterator's own
* {@link ListIterator#remove() remove} or
* {@link ListIterator#add(Object) add} methods, the iterator will throw a
* {@link ConcurrentModificationException}. Thus, in the face of
* concurrent modification, the iterator fails quickly and cleanly, rather
* than risking arbitrary, non-deterministic behavior at an undetermined
* time in the future.
* 通过 ArrayList 的 iterator() 方法,或者 listIterator(int) 方法返回的 iterator 都是 fail-fast(快速失效)的:如果在创建 iterator 之后,任何对 list 的结构性修改,都会抛出 ConcurrentModificationException,除了 iterator 本身的 remove() 和 add() 方法。因此,在面临并发对 list 的修改时,iterator 会快速而干净的失效,而不是在未来不确定的时间冒着任意的、不确定的风险。
* <p>Note that the fail-fast behavior of an iterator cannot be guaranteed
* as it is, generally speaking, impossible to make any hard guarantees in the
* presence of unsynchronized concurrent modification. Fail-fast iterators
* throw {@code ConcurrentModificationException} on a best-effort basis.
* Therefore, it would be wrong to write a program that depended on this
* exception for its correctness: <i>the fail-fast behavior of iterators
* should be used only to detect bugs.</i>
* 另外,需要注意的是,,iterator 的 fail-fast 行为是不能被保证的,,通常来说,在并发非同步对 list 的修改时,任何硬性的保证都是不可能的。fail-fast 会让 iterator 尽可能抛出 ConcurrentModificationException。因此,在程序中通过依赖抛出 ConcurrentModificationException 异常来保证自身的正常运行是错误的:fail-fast 行为只应该用来 debug。
* <p>This class is a member of the
* <a href="{@docRoot}/../technotes/guides/collections/index.html">
* Java Collections Framework</a>.
*
* @author Josh Bloch
* @author Neal Gafter
* @see Collection
* @see List
* @see LinkedList
* @see Vector
* @since 1.2
*/

public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable

ArrayList 的类属性及构造方法

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
/**
* Default initial capacity.
* 默认初始 capacity, 10
*/
private static final int DEFAULT_CAPACITY = 10;

/**
* Shared empty array instance used for empty instances.
* 对所有由无参构造函数创建的空实例共享的空数组对象
*/
private static final Object[] EMPTY_ELEMENTDATA = {};

/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == EMPTY_ELEMENTDATA will be expanded to
* DEFAULT_CAPACITY when the first element is added.
* 临时用来存储 ArrayList 中元素的数组。 ArrayList 对象的长度就是这个缓存数组的长度。任何空 ArrayList ,且 elementData == EMPTY_ELEMENTDATA,在添加第一个元素时,数组将被扩展到默认大小(DEFAULT_CAPACITY, 10)。
* (因为是临时的,所以用 transient 修饰,不会被序列化)
*/
private transient Object[] elementData;

/**
* The size of the ArrayList (the number of elements it contains).
* ArrayList 的 size(包含的元素的数量)。
* @serial
*/
private int size;

/**
* The maximum size of array to allocate.
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
* 允许分配给数组的最大的容量。
* 一些虚拟机实现会在数组预留一些 header words。
* 视图分配更大的数组可能会造成 OOM:所需数组大小超出虚拟机限制。
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

/**
* Constructs an empty list with the specified initial capacity.
* 通过指定初始 capacity 大小来构造一个空 list
* @param initialCapacity the initial capacity of the list
* @throws IllegalArgumentException if the specified initial capacity
* is negative
*/
public ArrayList(int initialCapacity) {
super();
// 初始容量小于零时,会抛出 IllegalArgumentException
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
// 初始化指定大小的临时对象数组
this.elementData = new Object[initialCapacity];
}

/**
* Constructs an empty list with an initial capacity of ten.
* 构造一个空 list,当向该 list 添加元素时,需先扩展数组到默认初始容量大小,即 10.
*/
public ArrayList() {
super();
this.elementData = EMPTY_ELEMENTDATA;
}

/**
* Constructs a list containing the elements of the specified
* collection, in the order they are returned by the collection's
* iterator.
* 按照 collection 迭代器返回元素的顺序构造包含指定 collection 的元素的 list。
* @param c the collection whose elements are to be placed into this list
* @throws NullPointerException if the specified collection is null
*/
public ArrayList(Collection<? extends E> c) {
// 如果 c 为 null,此处会抛 NPE
elementData = c.toArray();
size = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)
// 如果 elementData 不是对象数组,还需要将其复制到新的对象数组中。
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
阅读全文 »

Context

关于 HashMap, 有一件事儿一直很困惑,,在『Redis 核心历险』中是这样被提到的:

Redis 的字典相当于 Java 语言中的 HashMap,它是无序字典,内部存储了很多键值对。实现结构上与 Java 的 HashMap 也是一样的,都是”数组 + 链表”二维结构。第一维的 hash 的数组位置碰撞时,就会将碰撞的元素使用链表串接起来

嗯,第一句没啥问题,,然而”数组 + 链表”什么鬼?还有 hash 碰撞。。作者并没有详细解释这儿,应该默认是每个 Java 开发者都知道的知识点了。然而我却在这儿卡壳了,心慌慌。。趁着周末赶紧补补课。事实证明,这波补课是很有效果的,在这本书后边的内容中,hash 还将会一直出现。

Hash

阅读全文 »

好久好久之前的零碎知识点了…

关于Linux中的管道
1
2
// 列出当前目录下后缀为 conf 的文件
ls -lh | grep *.conf

Pipe:即 ls 和 grep 命令之间的**|(⇧+\),管道就是连接一个程序输出和另一个程序输入的通路**!!!

node.js 项目中的 package.json 文件的作用
阅读全文 »

LinkedList 的 DOC 注释

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
/**
* Doubly-linked list implementation of the {@code List} and {@code Deque}
* interfaces. Implements all optional list operations, and permits all
* elements (including {@code null}).
* 该双向链表实现了 List 接口和 Deque 接口。实现了 list 的所有可选操作,并且允许(添加)所有元素(包括 null)。
* <p>All of the operations perform as could be expected for a doubly-linked
* list. Operations that index into the list will traverse the list from
* the beginning or the end, whichever is closer to the specified index.
* 对于该双向链表,所有操作都是可预期的。如果对 list 的操作涉及到指定索引位置的元素,那么将判断指定的索引离 begin 更近,还是离 end 更近。这样能进行更少次数的迭代,性能更好。
* <p><strong>Note that this implementation is not synchronized.</strong>
* If multiple threads access a linked list concurrently, and at least
* one of the threads modifies the list structurally, it <i>must</i> be
* synchronized externally. (A structural modification is any operation
* that adds or deletes one or more elements; merely setting the value of
* an element is not a structural modification.) This is typically
* accomplished by synchronizing on some object that naturally
* encapsulates the list.
* 需要注意的是 LinkedList 类并不是线程安全的。如果多线程同时访问 linked list,而且至少一个线程会修改 list 的结构,那么它的外部必须使用 synchronized 关键字。(结构性修改指的是 插入(add)或者删除(remove)操作,修改一个元素的值并不是结构性修改)。这通常通过同步(synchronized 修饰)一个 封装了 list 的对象来实现。
* If no such object exists, the list should be "wrapped" using the
* {@link Collections#synchronizedList Collections.synchronizedList}
* method. This is best done at creation time, to prevent accidental
* unsynchronized access to the list:<pre>
* List list = Collections.synchronizedList(new LinkedList(...));</pre>
* 如果手头儿没有这样的对象,list 应该被 Collections.synchronizedList() 包裹起来。最好在初始化 LinkedList 对象时候就这样去做,以免不可预知的对 list 的非同步访问。
* <p>The iterators returned by this class's {@code iterator} and
* {@code listIterator} methods are <i>fail-fast</i>: if the list is
* structurally modified at any time after the iterator is created, in
* any way except through the Iterator's own {@code remove} or
* {@code add} methods, the iterator will throw a {@link
* ConcurrentModificationException}. Thus, in the face of concurrent
* modification, the iterator fails quickly and cleanly, rather than
* risking arbitrary, non-deterministic behavior at an undetermined
* time in the future.
* 通过LinkedList 的 iterator() 方法,或者 listIterator(int) 方法返回的 iterator 都是 fail-fast(快速失效)的:如果在创建 iterator 之后,任何对 list 的结构性修改,都会抛出 ConcurrentModificationException,除了 iterator 本身的 remove() 和 add() 方法。因此,在面临并发对 list 的修改时,iterator 会快速而干净的失效,而不是在未来不确定的时间冒着任意的、不确定的风险。
* <p>Note that the fail-fast behavior of an iterator cannot be guaranteed
* as it is, generally speaking, impossible to make any hard guarantees in the
* presence of unsynchronized concurrent modification. Fail-fast iterators
* throw {@code ConcurrentModificationException} on a best-effort basis.
* Therefore, it would be wrong to write a program that depended on this
* exception for its correctness: <i>the fail-fast behavior of iterators
* should be used only to detect bugs.</i>
* 另外,需要注意的是,,iterator 的 fail-fast 行为是不能被保证的,,通常来说,在并发非同步对 list 的修改时,任何硬性的保证都是不可能的。fail-fast 会让 iterator 尽可能抛出 ConcurrentModificationException。因此,在程序中通过依赖抛出 ConcurrentModificationException 异常来保证自身的正常运行是错误的:fail-fast 行为只应该用来 debug。
* <p>This class is a member of the
* <a href="{@docRoot}/../technotes/guides/collections/index.html">
* Java Collections Framework</a>.
*
* @author Josh Bloch
* @see List
* @see ArrayList
* @since 1.2
* @param <E> the type of elements held in this collection
*/
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable

LinkedList 的类属性及构造方法

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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
transient int size = 0;

/**
* Pointer to first node. // 指向 LinkedList 的第一个节点
* Invariant: (first == null && last == null) ||
* (first.prev == null && first.item != null)
*/
transient Node<E> first;

/**
* Pointer to last node. // 指向 LinkedList 的最后一个节点
* Invariant: (first == null && last == null) ||
* (last.next == null && last.item != null)
*/
transient Node<E> last;

/**
* Constructs an empty list.
* 构造一个空 list
*/
public LinkedList() {
}

/**
* Constructs a list containing the elements of the specified
* collection, in the order they are returned by the collection's
* iterator.
* 通过指定的 collection 构造一个包含元素的 list,其顺序由 collection 的迭代器决定。
* @param c the collection whose elements are to be placed into this list
* @throws NullPointerException if the specified collection is null
*/
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}

/**
* Appends all of the elements in the specified collection to the end of
* this list, in the order that they are returned by the specified
* collection's iterator. The behavior of this operation is undefined if
* the specified collection is modified while the operation is in
* progress. (Note that this will occur if the specified collection is
* this list, and it's nonempty.)
* 将指定的 collection 的所有元素拼接到 list 的末尾元素之后,顺序由指定的 collection 的迭代器决定。在执行该操作(addAll())的时候,如果 collection 被修改了,该操作的行为将不可预知(请注意,如果指定的 collection 正好是该 list 对象自己,那么这种情况将会发生)。
* @param c collection containing elements to be added to this list
* @return {@code true} if this list changed as a result of the call
* @throws NullPointerException if the specified collection is null
*/
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}

/**
* Inserts all of the elements in the specified collection into this
* list, starting at the specified position. Shifts the element
* currently at that position (if any) and any subsequent elements to
* the right (increases their indices). The new elements will appear
* in the list in the order that they are returned by the
* specified collection's iterator.
* 从指定的位置开始将指定 collection 中的所有元素插入到该 list 中。指定位置的元素(如果有的话)及其右边的元素将会右移(原索引值增大)。新的元素将会以指定 collection 的迭代器 返回的顺序出现在 list 中。
* @param index index at which to insert the first element
* from the specified collection
* @param c collection containing elements to be added to this list
* @return {@code true} if this list changed as a result of the call
* @throws IndexOutOfBoundsException {@inheritDoc}
* @throws NullPointerException if the specified collection is null
*/
public boolean addAll(int index, Collection<? extends E> c) {
// 判断索引是否非法
checkPositionIndex(index);

// 将 collection 转为数组
Object[] a = c.toArray();
int numNew = a.length;
// 如果为空数组,返回 false
if (numNew == 0)
return false;

Node<E> pred, succ;
// 如果索引值与容器的大小相同,即当前 LinkedList 实例中无任何元素
if (index == size) {
succ = null;
pred = last;
} else {
succ = node(index);
pred = succ.prev;
}

for (Object o : a) {
@SuppressWarnings("unchecked") E e = (E) o;
// 构建 Node 节点对象 newNode
Node<E> newNode = new Node<>(pred, e, null);
// 如果指定位置的元素位于列表头部,则 newNode 为 first 节点
if (pred == null)
first = newNode;
else
// 否则,前一个节点的 next 属性指向 newNode
pred.next = newNode;
// pred 指向 newNode,开始下一轮遍历(相当于常规 for 循环的 i++)
pred = newNode;
}

// 如果原 list 为空列表,则经过遍历之后,last 指向 pred
if (succ == null) {
last = pred;
} else {
// 如果指定索引位置有值,遍历添加 collection 中的元素之后,指定索引位置的元素及其右侧的所有元素统一右移,,将新增的所有元素中的最后一个元素的 next 指向 右移的元素中的第一个元素;右移的元素中的第一个元素的 prev 属性指向新增元素中的最后一个。
pred.next = succ;
succ.prev = pred;
}

// 原 size 加上 新增元素的数量
size += numNew;
// list 的结构性更改次数 +1
modCount++;
return true;
}

private void checkPositionIndex(int index) {
if (!isPositionIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
/**
* Tells if the argument is the index of a valid position for an
* iterator or an add operation.
* 判断参数代表的索引位置在 iterator 或者 add() 中是否可用
*/
private boolean isPositionIndex(int index) {
// 不小于零,且不大于 size
return index >= 0 && index <= size;
}

/**
* Returns the (non-null) Node at the specified element index.
* 返回指定位置的非空节点
* 此处遍历 list 查找指定位置的元素时,先判断 index 距离 begin 节点近还是 end 节点。如此可以减少遍历次数,有助于性能表现。大概这就是维护成双向列表而非单向列表的原因吧。
*/
Node<E> node(int index) {
// assert isElementIndex(index);

// 指定的索引值是否小于中位数(翻译成人话就是,判断索引是否在列表的前半段)
if (index < (size >> 1)) {
Node<E> x = first;
// 就是硬遍历,直到指定索引位置
// 因为 i < index,所以会遍历到指定索引位置的前一个节点,这时取其 next 指向的节点即可。
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}

// 私有静态内部类
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;

Node(Node<E> prev, E element, Node<E> next) {
// item 为当前节点的值
this.item = element;
// next 指向当前节点的下一个节点
this.next = next;
// prev 指向当前节点的前一个节点
this.prev = prev;
}
}

关于关键字 transient,,
一个对象只要实现了 Serializable 接口,该对象就可以被序列化。然而在实际开发过程中,常常会遇到这样的问题,该类有些属性需要序列化,其他属性不需要被序列化。例如一个用户有一些敏感信息(如密码,银行卡号等),为了安全起见,不希望在网络操作(主要涉及序列化)中被传输,这些信息对应的变量就可以加上 transient 关键字,这样变量的生命周期仅存在于调用者的内存中而不会被写到磁盘里持久化。)

阅读全文 »

Context

果然,『深入理解Java虚拟机』这本书读一遍是远远不够的,温故总能知新~今天要写的是先行发生原则,说来惭愧,,好像读第一遍的时候并没有太深刻的印象,昨晚再读的时候就拍大腿了:醍醐灌顶啊!!所以,今天还真得扮演搬运工的角色。

啥是先行发生原则

说到先行发生原则,还得再往前倒倒,说下 Java 内存模型的特征。
Java内存模型是围绕着在并发过程中如何处理原子性、可见性和有序性这个3个特征来建立的。

  • 原子性(Atommicity): 由 Java 内存模型来直接保证原子性变量的操作,包括 read、load、use、assign、store、write, 大致可以认为基本数据类型的访问读写是具有原子性的(long、double 的非原子性协定除外)。如果场景需要更大范围的保证原子性,则需要 lockunlock 操作。反映到 Java 代码中就是同步块– synchronized 关键字。因此,所以 synchronized 块之间的操作也具备原子性。
  • 可见性(Visibility): Java 内存模型是通过在变量修改后将新值同步回主存,在变量读取前从主内存刷新变量值这种依赖主内存作为传递媒介的方式来实现可行性的,无论是普通变量还是 volatile 变量都是如此。只不过,volatile 关键字能保证新值被立即同步回主内存,以及每次使用前从主内存刷新,普通变量则不能保证这一点。
    除此之外,synchronized 和 final 关键字也可以保证变量的可见性。同步快的可见性是由『对一个变量执行 unlock 操作之前,必须先把此变量同步回主内存中(执行 store 和 write 操作)』这条规则获得的。而 final 关键字的可见性是指: 被 final 修饰的字段在构造器中一旦初始化完成,并且构造器没有把 this 的引用传递出去^1,那么在其他线程中就能看见 final 字段的值。(最初以为是因为 final 修饰的变量不能被修改,所以导致所有线程读到的值都是一样的,不变的,,然鹅并不是[^2])。
  • 有序性(Ordering): Java 程序中天然的有序性可以总结为一句话:如果在本线程内观察,所有操作都是有序的;如果在一个线程中观察另外一个线程,所有的操作都是无序的。前半句是指“线程内表现为串行的语义(Within-Thread As-If Serial Semantics)”,后半句是指“指令重排序”和“工作内存和主内存同步延迟”线程。
    volatile 关键字本身就含有禁止指令重排的语义,而 synchronized 则是由『一个变量在同一时刻只允许一条线程对其进行 lock 操作。』这条规则获得的,这条规则决定了持有同一个锁( lock 操作的变量)的两个同步块只能串行的进入。
阅读全文 »

Context

最近一直没有更新文章是因为过得很充实,,
一方面,现在在做小程序的线上商城,小程序这边倒没什么难的,难的是服务器端怎么设计架构,涉及到不同的客户,不同的客户门店组织架构又不一样,硬件条件也不一样,商品也不一样,还需要跟 ERP 的同事进行沟通。。
另一方面,一直计划在房子合同到期(2019-04-04)之后换工作,所以积极准备面试,查漏补缺什么的。去年12月份时候,买了『深入理解Java虚拟机』和『Java并发编程的艺术』。前者全是干货,干到没法儿写博客做笔记,否则就是在照搬书上的内容了。后者,,额,有点晦涩,没有读下去的欲望,然后就放在省图计算机科学分类下的书架上了,嗯,上周末去省图发现那本书已经不见了🤣。
对了,与此同时,,发现了 vue-element-admin 框架,一直想试试 webpack,然后就在不忙的时候慢慢重构现有的后台页面。用封装好的组件写页面真省心,爽的飞起~

嗯,,今天的主要内容来自『深入理解Java虚拟机』的第五部分第一章–Java内存模型与线程

这是第二次读这一部分了,,第一次读的时候大水漫灌囫囵吞枣,全局把握(大误)。这次读的时候主要的是抠细节,温故知新,受益匪浅!下面进入正题儿~

Java内存模型与线程

阅读全文 »

🕯🕯🕯
我上小学,你出差;之后我开始住校,初中两周回家一次,每次两天;高中四周回家一次,每次不到36小时;我上大学了,你退休了,我半年回家一次。
如果你还在..


  1. 你认为最完美的快乐是怎样的?
    内心的平静
  2. 你最希望拥有哪种才华?
    还是将问题抽象成代码的能力
  3. 你最恐惧的是什么?
    变老
  4. 你目前的心境怎样?
    邻居不懂事儿,挪动椅子和桌子的时候硬扯,发出刺耳的声音,,MMP
    这一年过得真特么快!!
  5. 还在世的人中你最钦佩的是谁?
    由埃隆马斯克临时改为『丁香医生』,,不会删稿,对每一个字负责,欢迎来告。
  6. 你认为自己最伟大的成就是什么?
    大概是全马323吧
  7. 你自己的哪个特点让你最觉得痛恨?
    亲密关系里把握不好度?
  8. 你最喜欢的旅行是哪一次?
    2018年端午节的苏州之旅,,准确来说是喜欢苏州
  9. 你最痛恨别人的什么特点?
    不知何为个人边界!
  10. 你最珍惜的财产是什么?
    个人信用
  11. 你最奢侈的是什么?
    很幸运,,如此幸运
  12. 你认为程度最浅的痛苦是什么?
    浅的都不痛苦,痛苦都不浅
  13. 你认为哪种美德是被过高的评估的?
    这个话题一两句说不清楚,,容易引战。
  14. 你最喜欢的职业是什么?
    还是程序员,,能用技术改变世界的职业都是我喜欢的
  15. 你对自己的外表哪一点不满意?
    右边的眉毛,,为什么你不能听话一点.Smile.
  16. 你最后悔的事情是什么?
    大学时太混沌,而且当时完全不自知..
  17. 还在世的人中你最鄙视的是谁?
    杠精们,,Love&Peace
  18. 你最喜欢男性身上的什么品质?
    能为自己说过的话、做过的事负责
  19. 你使用过的最多的单词或者是词语是什么?
    Clear?(PS: 跟别人沟通时,不确定对方是否完全接收到自己想传达的信息,会问一句Clear?Clear = 我表达清楚了么?+你GET到了么?)
  20. 你最喜欢女性身上的什么品质?
    真∙男女平等
  21. 你最伤痛的事是什么?
    形同陌路
  22. 你最看重朋友的什么特点?
    言行一致,,不整那些片儿汤话
  23. 你这一生中最爱的人或东西是什么?
    Macbook Pro 15’’ (Especially 32G),,心心念
  24. 你希望以什么样的方式死去?
    安乐死,或者一场猝不及防的意外
  25. 何时何地让你感觉到最快乐?
    写了一大段代码,一次就能跑起来,不用调试的那种
  26. 如果你可以改变你的家庭一件事,那会是什么?
    早点让我知道电脑不光可以玩游戏..
    大家身体健康就好,别的真不奢求
  27. 如果你能选择的话,你希望让什么重现?
    小我:早上被我爸的录音机(装磁带的那个)叫醒,我该起床吃饭上学了。
    大我:太阳系重回三维宇宙。
  28. 你的座右铭是什么?
    生命的过程,无论是阳春白雪,青菜豆腐,我都得尝尝是什么滋味,才不枉来走这么一遭。

Extra..
2018年的 Keyword 是??

阅读全文 »

shadowssocks日志级别配置

Context

这个,,闲得无聊,看了看shadowsocks.log文件👇

1
2
3
4
5
6
# 江湖险恶啊,,请求IP地址处理了下下。
2018-11-20 07:45:21 INFO connecting mtalk.google.com:5228 from 1*4.2*6.2*0.10:42373
2018-11-20 07:46:21 INFO connecting mtalk.google.com:5228 from 1*4.2*6.2*0.10:42376
2018-11-20 07:46:39 INFO connecting notifications.google.com:443 from 1*4.2*6.2*0.10:42377
2018-11-20 07:47:22 INFO connecting mtalk.google.com:5228 from 1*4.2*6.2*0.10:42239
2018-11-20 07:47:58 INFO connecting clients4.google.com:443 from 1*4.2*6.2*0.10:42346

日志信息中没啥特别有用的信息,,关键是这么长时间了,日志文件已经很大了👇

阅读全文 »

Context

这次是自己的需求,刚好周六,闲来无事来公司加班,好好总结下。

自己做了个小程序,用来收集平时的位置信息,还有微信步数什么的,租的国外的服务器,,翻墙和部署项目两不误,顺便做做linux的练习,完美~ 在初期,部署项目以及一些静态资源时,经常404,肯定nginx哪里配置错了,,直接在linux服务器上用命令行翻日志又很麻烦,ssh还总断(扶额)。把nginx日志文件当静态资源访问好啦,这样直接在浏览器就可以访问,完美~这一步并不难,好好配置nginx.conf中的location属性就好,略过不表。

但我担心的是访问路径暴露了怎么办。。有必要加上基础的访问限制,验证用户名密码什么的。还要自己写个页面?在数据库配置用户名密码?太不优雅了。还好有nginx的auth_basic模块,完美解决问题~

这之后,有一次闲来没事,翻翻nginx日志,,呦,被吓一跳!👇

阅读全文 »

Context

按照惯例先说上下文环境,,
是这样,,公司需要做个功能:

对ERP用户和公司公众号用户做映射,然后向该用户推送相应角色的经营日报模板消息。

实现流程:
1.通过微信接口生成带参二维码;
2.用户用微信扫一扫功能扫描生成的带参二维码;
3.获取二维码中的客户识别码和用户的openid;
4.被动回复用户文本消息(附超链接),引导用户完成绑定过程。

Tips

总的来说是没啥难度的,,过程还算顺利。有两点值得一提,,

阅读全文 »
0%