1. 同步容器List
1.1 Vector
概念: Vector类称为向量类或矢量类,可以实现可扩容的数组,支持使用索引进行访问,Vector的容量可以根据需要增大或缩小。
- 构造:可以在构造向量的时候指定初始容量和默认增量。
new Vector():默认初始容量为10,默认增量为0。new Vector(10):指定初始容量为,默认增量为0。new Vector(10, 5):指定初始容量和满容后的增量。- 增量越小,内存管理的效率越高,但执行开销越大,因为执行内存分配的次数将越多。
- 增量越大,执行内存分配的次数将越少,但如果没有用完分配的所有空间,将浪费内存。
- 方法:
boolean add(E e):尾部追加一个元素。void add(int index, E element):在index位置插入一个元素。E set(int index, E element):将index位置上的元素修改为E。int capacity():返回向量的最大容量。int size():返回向量中当前元素的个数。boolean contains(Object o):返回向量中是否包含元素o。int indexOf(Object o):返回向量中元素o所在的位置,不存在返回-1。boolean removeElement(Object obj):删除向量中指定的元素。E remove(int index):返回并删除向量中index位置上的元素。void clear():删除向量中所有的元素。
源码: /javase-advanced/
- src:
c.y.thread.collection.SynchronizedListTest.vectorByDebug()
/**
* @author yap
*/
public class SynchronizedListTest {
@Test
public void vectorByDebug() {
Vector<String> vector = new Vector<>();
System.out.println(vector.add("zhao-si"));
vector.add(0, "liu-neng");
vector.add(1, "da-jiao");
System.out.println(vector.set(1, "chang-gui"));
System.out.println(vector.get(1));
System.out.println(vector.capacity());
System.out.println(vector.size());
System.out.println(vector.contains("da-jiao"));
System.out.println(vector.indexOf("chang-gui"));
System.out.println(vector.firstElement());
System.out.println(vector.lastElement());
System.out.println(vector.removeElement("liu-neng"));
System.out.println(vector.remove(0));
vector.clear();
}
}
1.2 SynchronizedList
概念: Collections中提供了一个方法可以将异步的List容器转换成一个同步的List容器:
- 方法:
static <T> List<T> synchronizedList(List<T> list)
源码: /javase-advanced/
- src:
c.y.thread.collection.SynchronizedListTest.synchronizedList()
/**
* @author yap
*/
public class SynchronizedListTest {
@Test
public void synchronizedList() {
ArrayList<String> arrayList = new ArrayList<>();
List<String> list = Collections.synchronizedList(arrayList);
list.add("zhao-si");
System.out.println(list.get(0));
}
}
1.3 CopyOnWriteArrayList
概念: CopyOnWriteArrayList的特点是写时进行加锁复制,读时不加锁,适用于读线程远远多于写线程的情景。
源码: /javase-advanced/
- src:
c.y.thread.collection.SynchronizedListTest.copyOnWriteArrayList()
/**
* @author yap
*/
public class SynchronizedListTest {
@Test
public void copyOnWriteArrayList() {
List<String> list = new CopyOnWriteArrayList<>();
list.add("zhao-si");
System.out.println(list.get(0));
}
}
2. 同步容器Map
2.1 Hashtable
概念: Hashtable中的大部分方法都是线程同步的,而且不支持null值,其余和HashMap的使用相似。
源码: /javase-advanced/
- src:
c.y.thread.collection.SynchronizedMapTest.hashtable()
/**
* @author yap
*/
public class SynchronizedMapTest {
@Test
public void hashtable() {
Hashtable<String, Object> hashtable = new Hashtable<>();
hashtable.put("name", "zhao-si");
hashtable.put("gender", "male");
hashtable.put("age", 18);
hashtable.forEach((key, value) -> {
System.out.println(value);
});
System.out.println(hashtable.size());
}
}
2.2 SynchronizedHashMap
概念: Collections中提供了一个方法可以将异步的Map容器转换成一个同步的Map容器:
- 方法:
static <K,V> Map<K,V> synchronizedMap(Map<K,V> m)
源码: /javase-advanced/
- src:
c.y.thread.collection.SynchronizedMapTest.synchronizedHashMap()
/**
* @author yap
*/
public class SynchronizedMapTest {
@Test
public void synchronizedHashMap() {
HashMap<String, Object> hashMap = new HashMap<>(3);
Map<String, Object> map = Collections.synchronizedMap(hashMap);
map.put("name", "zhao-si");
map.forEach((k, v) -> {
System.out.println(v);
});
}
}
2.3 ConcurrentHashMap
概念: ConcurrentHashMap采用分段锁的技术,适用于并发量比较高的情况。
- ConcurrentHashMap底层使用的是Node数组 + 链表 + 红黑树的结构完成的。
- ConcurrentHashMap在添加元素的时候,会根据key值的hash值决定放到Node数组的哪个位置,取出元素的时候也会在相应的位置上取出值。
- key值的hash值相同的时候,会在Node数组的某位置上形成链表。
- Node数组的某位置上的链表过长时(大于8),会被转换成红黑树结构。
put(K, V):- 计算key的哈希值,并得到对应数组的索引i。
- 如果table为空,初始化table(Node数组)。
- 判断是否发生hash冲突:
- 如果不冲突:直接创建新节点,并CAS插入数组对应位置。
- 如果冲突且hash值为MOVED,则使用多线程对该链进行扩容(提高效率),并返回扩容后的table。
- 否则加锁,将新节点插入到数组对应位置上的链表或红黑树的尾部。
- 计数器自增1,有可能出发扩容操作。
源码: /javase-advanced/
- src:
c.y.thread.collection.SynchronizedMapTest.concurrentHashMap()
/**
* @author yap
*/
public class SynchronizedMapTest {
@Test
public void concurrentHashMap() {
Map<Object, Object> map = new ConcurrentHashMap<>(3);
map.put("name", "zhao-si");
map.forEach((k, v) -> {
System.out.println(v);
});
}
}
2.4 ConcurrentSkipListMap
概念: ConcurrentSkipListMap底层使用的是跳表数据结构,在高并发情况下可以更快速的找到元素,且它可以排序,用于替代concurrentTreeMap(不存在,因为树结构的CAS太复杂)。
源码: /javase-advanced/
- src:
c.y.thread.collection.SynchronizedMapTest.concurrentSkipListMap()
/**
* @author yap
*/
public class SynchronizedMapTest {
@Test
public void concurrentSkipListMap() {
Map<Object, Object> map = new ConcurrentSkipListMap<>();
map.put("name", "zhao-si");
map.forEach((k, v) -> {
System.out.println(v);
});
}
}
3. 同步容器Queue
3.1 ConcurrentLinkedQueue
概念: ConcurrentLinkedQueue底层使用的是CAS操作,高并发时效率比较高。
源码: /javase-advanced/
- src:
c.y.thread.collection.SynchronizedQueueTest.concurrentLinkedQueue()
@Test
public void concurrentLinkedQueue() {
Queue<String> queue = new ConcurrentLinkedQueue<>();
queue.add("zhao-si");
System.out.println(queue.poll());
}
3.2 LinkedBlockingQueue
概念: LinkedBlockingQueue是一个由单链表结构构成的单向阻塞队列,LinkedBlockingQueue是读写分离的,添加和读取元素分别有各自的锁。
- 构造:
LinkedBlockingQueue():默认队列容量为Integer.MAX_VALUE。- 尽量不要使用这种方式,因为容易导致队列还没满,内存已经满了。
LinkedBlockingQueue(int capacity):指定队列容量,推荐指定。
- 方法:
E take():获取元素,当队列为空时阻塞。void put(E e):添加元素,当队列满时阻塞。boolean isEmpty():返回队列是否为空。
源码: /javase-advanced/
- src:
c.y.thread.collection.SynchronizedQueueTest.linkedBlockingQueue()
@Test
public void linkedBlockingQueue() {
BlockingQueue<Integer> queue = new LinkedBlockingQueue<>(5);
System.out.println(queue.isEmpty());
new Thread(() -> {
try {
while (true) {
TimeUnit.SECONDS.sleep(2L);
int data = new Random().nextInt(100);
queue.put(data);
System.out.println("produce: " + data);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "producer").start();
for (int i = 0, j = 5; i < j; i++) {
new Thread(() -> {
while (true) {
try {
System.out.println(Thread.currentThread().getName() + " : " + queue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "consumer-" + i).start();
}
}
3.3 LinkedBlockingDeque
概念: LinkedBlockingDeque是由一个双链表结构构成的双向阻塞队列,因为多了一个操作队列的入口,所以在多线程同时入队时,减少了一半的竞争。
- 构造:
LinkedBlockingDeque():默认队列容量为Integer.MAX_VALUE。- 尽量不要使用这种方式,因为容易导致队列还没满,内存已经满了。
LinkedBlockingDeque(int capacity):指定队列容量,推荐指定。
- 方法:LinkedBlockingDeque相对于其他阻塞队列,多出了一些首尾相关方法,比如:
void addFirst(E e):头部添加元素。void addLast(E e):尾部添加元素。E peekFirst():查看但不移除头。E peekLast():查看但不移除尾。
源码: /javase-advanced/
- src:
c.y.thread.collection.SynchronizedQueueTest.linkedBlockingDeque()
@Test
public void linkedBlockingDeque() {
LinkedBlockingDeque<String> queue = new LinkedBlockingDeque<>();
queue.addFirst("zhao-si");
queue.addLast("liu-neng");
System.out.println(queue.peekFirst());
System.out.println(queue.peekLast());
System.out.println(queue);
}
3.4 ArrayBlockingQueue
概念: ArrayBlockingQueue底层是数组实现的。
- 构造:
ArrayBlockingQueue(int capacity):必须指定初始容量,且这个容量值永远不变。 - 方法:
E take():获取元素,当队列为空时阻塞。void put(E e):添加元素,当队列满时阻塞。
源码: /javase-advanced/
- src:
c.y.thread.collection.SynchronizedQueueTest.arrayBlockingQueue()
@SneakyThrows
@Test
public void arrayBlockingQueue() {
BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);
for (int i = 0, j = 10; i < j; i++) {
queue.put(i);
}
System.out.println(queue.size());
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(3L);
System.out.println(queue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
// blocking and wait for consumer
queue.put(250);
System.out.println("over...");
}
3.5 DelayQueue
概念: DelayQueue可以按照延迟时间来进行任务调度,声明的时候需要泛型指定一个实现了Delayed接口的任务类,并重写:
long getDelay(TimeUnit unit):负责根据时间单位来获取延迟的值。int compareTo(Delayed o):负责根据延迟的值来进行比较和排序。
源码: /javase-advanced/
- src:
c.y.thread.collection.SynchronizedQueueTest.delayQueue()
/**
* @author yap
*/
public class SynchronizedQueueTest {
private static class MyTask implements Delayed {
private String taskName;
private Long timestamp;
MyTask(String taskName, Long timestamp) {
this.taskName = taskName;
this.timestamp = timestamp;
}
@Override
public long getDelay(TimeUnit unit) {
return unit.convert(timestamp - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
}
@Override
public int compareTo(Delayed o) {
return Long.compare(getDelay(TimeUnit.MILLISECONDS), o.getDelay(TimeUnit.MILLISECONDS));
}
@Override
public String toString() {
return taskName + ": " + timestamp;
}
}
@SneakyThrows
@Test
public void delayQueue() {
BlockingQueue<MyTask> queue = new DelayQueue<>();
long now = System.currentTimeMillis();
queue.put(new MyTask("task1", now + 3));
queue.put(new MyTask("task4", now + 1));
queue.put(new MyTask("task5", now + 2));
for (int i = 0, j = queue.size(); i < j; i++) {
System.out.println(queue.take());
}
}
@SneakyThrows
@After
public void after() {
System.out.println(System.in.read());
}
}
3.6 SynchronousQueue
概念: SynchronousQueue的初始容量为0,它不是用来装数据的,是用来向其他线程传递数据的。
- 构造:
SynchronousQueue():初始容量为0。SynchronousQueue(boolean fair):指定公平/非公平模式。
- 方法:
E take():获取元素,当队列为空时阻塞,什么时候有人put值,它什么时候take到值。void put(E e):添加元素,当队列满时阻塞。
- SynchronousQueue不能调用
add(),直接报错。
源码: /javase-advanced/
- src:
c.y.thread.collection.SynchronizedQueueTest.synchronousQueue()
@SneakyThrows
@Test
public void synchronousQueue() {
BlockingQueue<String> queue = new SynchronousQueue<>();
new Thread(() -> {
try {
System.out.println(queue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
TimeUnit.SECONDS.sleep(2L);
queue.put("zhao-si");
System.out.println("size: " + queue.size());
}
3.7 LinkedTransferQueue
概念: LinkedTransferQueue是一个由链表构成的无界队列,采用预占位模式。
- 预占位模式下的
take():- 如果队列不为空,直接take走数据。
- 如果队列为空,生成一个null值节点入队,消费者在这个节点上等待。
- 当生产者生产数据并入队时,发现队列中存在一个null值节点,则将数据直接填充到这个节点中。
- 唤醒该节点上的消费者,消费者取走数据,从调用的方法返回。
- 方法:
void transfer(E e):向队列传递数据并阻塞,直到有人将数据take()走,它才继续运行。void put(E e):向队列传递数据但不阻塞,继续向下运行。
源码: /javase-advanced/
- src:
c.y.thread.collection.SynchronizedQueueTest.linkedTransferQueue()
@Test
public void linkedTransferQueue() {
TransferQueue<String> queue = new LinkedTransferQueue<>();
new Thread(() -> {
try {
System.out.println("consumer start...");
TimeUnit.SECONDS.sleep(2L);
System.out.println(queue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "consumer").start();
new Thread(() -> {
try {
System.out.println("producer start...");
queue.transfer("zhao-si");
// queue.put("zhao-si");
System.out.println("producer继续运行...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "producer").start();
}