容器
Vector<T>、Hashtable<K,V> ,自帶加鎖(synchronized),線程安全。
static Map<UUID,UUID> mm = Collections.synchronizedMap(new HashMap<>());
Collections.synchronizedMap 源碼
final Object mutex; // Object on which to synchronize
public int size() {
synchronized (mutex) {return m.size();}
}
可以看到,Collections.synchronizedMap 鎖的粒度變小了,而 hashtable 的鎖是在 方法上面的。
ConcurrentHashMap 插入的效率低於 hashtable、hashmap 。 讀的效率高於 hashtable、hashmap。
static List tick = new ArrayList();
static {
for(int i = 0;i < 100;i++){
tick.add(i);
}
System.out.println(" tick end");
}
public static void main(String[] args) {
// StampedLock
// CountDownLatch
MyThread m = new MyThread();
// WeakHashMap
for(int i = 0;i< 10;i++){
new Thread(()->{
while(tick.size() > 0){
System.out.println(Thread.currentThread().getName() + " 移除 : " + tick.remove(0));
}
},"t" + i).start();
}
}
出現移除多餘存儲的情況,相當於超售
解決(效率不高):
static List tick = new ArrayList<>();
static {
for(int i = 0;i < 100;i++){
tick.add(i);
}
System.out.println(" tick end");
}
public static void main(String[] args) {
// StampedLock
// CountDownLatch
MyThread m = new MyThread();
// WeakHashMap
for(int i = 0;i< 10;i++){
new Thread(()->{
while (true){
synchronized (tick){
if(tick.size() > 0){
System.out.println(Thread.currentThread().getName() + " 移除 : " + tick.remove(0));
}else{
break;
}
}
}
},"t" + i).start();
}
}
改成Vector:
static Vector<String> tick = new Vector<>();
static {
for(int i = 0;i < 100;i++){
tick.add(i + "");
}
System.out.println(" tick end");
}
public static void main(String[] args) {
// StampedLock
// CountDownLatch
MyThread m = new MyThread();
// WeakHashMap
for(int i = 0;i< 10;i++){
new Thread(()->{
while(tick.size() > 0){
System.out.println(Thread.currentThread().getName() + " 移除 : " + tick.remove(0));
}
},"t" + i).start();
}
}
依舊會超售。雖然Vector 是線程安全的,size()、remove() 也都是安全的,但是是單獨的安全,合在一起的時候就不是原子性的了。
所以換成synchronized,可以解決,效率也不高
new Thread(()->{
while(true){
synchronized (tick){
if(tick.size() > 0){
System.out.println(Thread.currentThread().getName() + " 移除 : " + tick.remove(0));
}else{
break;
}
}
}
},"t" + i).start();
Queue
static Queue<String> tick = new ConcurrentLinkedQueue<>();
static {
for(int i = 0;i < 100;i++){
tick.add(i + "");
}
System.out.println(" tick end");
}
public static void main(String[] args) {
// StampedLock
// CountDownLatch
MyThread m = new MyThread();
// WeakHashMap
for(int i = 0;i< 10;i++){
new Thread(()->{
while(true){
/**
* poll
* 取出並刪除隊列頭部的值,即第一個。原子操作。
* 沒有則返回null
*/
String r = tick.poll();
if(r == null) break;
System.out.println(Thread.currentThread().getName() + " 移除 : " + r);
}
},"t" + i).start();
}
}
多線程單個元素: Queue<String> tick = new ConcurrentLinkedQueue<>();
不需要有重複的可以考慮set:Set<String> strings = new ConcurrentSkipListSet<>();
有重複也可以考慮 list:List<String> list = new CopyOnWriteArrayList<>();
多線程推薦:Queue
poll 源碼
public E poll() {
restartFromHead:
for (;;) {
for (Node<E> h = head, p = h, q;;) {
E item = p.item;
if (item != null && p.casItem(item, null)) {
// Successful CAS is the linearization point
// for item to be removed from this queue.
if (p != h) // hop two nodes at a time
updateHead(h, ((q = p.next) != null) ? q : p);
return item;
}
else if ((q = p.next) == null) {
updateHead(h, p);
return null;
}
else if (p == q)
continue restartFromHead;
else
p = q;
}
}
}
CAS 實現原理。
一個線程,可以使用hashmap、linklist、arraylist
高併發情況但是持續時間短使用 ConcurrentHashMap、ConcurrentMap、ConcurrentLinkedQueue
代碼運行時間較長、併發不多使用 synchronized
ConcurrentSkipListMap 高併發排序
ConcurrentHashMap 高併發無須
ConcurrentSkipListMap 跳錶map。 最底層存儲的是鏈表數據,數據量大的時候,提出一層,還是很大再提出一層。查找的時候查找最上層的,進行區間 查找。有點像B+Tree
CopyOnWriteArrayList 寫時加鎖,讀時不加鎖。
在寫時,進行數組copy一份,數組長度+1,然後再進行添加。寫完之後將之前的引用指到新的上。
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
final void setArray(Object[] a) {
array = a;
}
BlockingQueue 無界阻塞隊列
BlockingQueue<String> blockingQueue = new LinkedBlockingQueue<>();
put 進行put。但是會拋出異常。如果傳入的時null 拋出空指針異常。否則進行鎖定,然後進行插入,如果空間不足則會進行阻塞。
/**
* Inserts the specified element at the tail of this queue, waiting if
* necessary for space to become available.
*
* @throws InterruptedException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
public void put(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
// Note: convention in all put/take/etc is to preset local var
// holding count negative to indicate failure unless set.
int c = -1;
Node<E> node = new Node<E>(e);
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
putLock.lockInterruptibly();
try {
/*
* Note that count is used in wait guard even though it is
* not protected by lock. This works because count can
* only decrease at this point (all other puts are shut
* out by lock), and we (or some other waiting put) are
* signalled if it ever changes from capacity. Similarly
* for all other uses of count in other wait guards.
*/
while (count.get() == capacity) {
notFull.await();
}
enqueue(node);
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
} finally {
putLock.unlock();
}
if (c == 0)
signalNotEmpty();
}
take 往外取,如果是空的,會阻塞
ArrayBlockingQueue 有界阻塞隊列,同 BlockingQueue 差不多,不過這個是有界的。
BlockingQueue<String> strings = new ArrayBlockingQueue<>(100);
Queue 和 List 的區別,Queue 添加了很多對線程友好的API: offer、peek、poll
peek 返回隊首的元素
poll: 返回隊首並移除元素
offer: 插入元素到隊尾,如果滿了也不阻塞
BlockingQueue : put、take. 線程阻塞.
DelayQueue 實現自 PriorityQueue
BlockingQueue<Taks> takss = new DelayQueue<Taks>();
用處:設計任務調度。
static class Taks implements Delayed{
String name = "";
long runningTime;
Taks(String name,long t){
this.name = name;
this.runningTime = t;
}
@Override
public int compareTo(Delayed o) {
if(this.getDelay(TimeUnit.MICROSECONDS) < o.getDelay(TimeUnit.MICROSECONDS)){
return -1;
}else if(this.getDelay(TimeUnit.MICROSECONDS) > o.getDelay(TimeUnit.MICROSECONDS)){
return 1;
}else{
return 0;
}
}
@Override
public long getDelay(TimeUnit unit) {
return unit.convert(runningTime - System.currentTimeMillis(),TimeUnit.MICROSECONDS);
}
}
main:
BlockingQueue<Taks> takss = new DelayQueue<Taks>();
long now = System.currentTimeMillis();
Taks t1 = new Taks("t1",now + 1500);
Taks t2 = new Taks("t1",now + 2500);
Taks t3 = new Taks("t1",now + 3500);
Taks t4 = new Taks("t1",now + 4500);
Taks t5 = new Taks("t1",now + 500);
takss.put(t1);
takss.put(t2);
takss.put(t3);
takss.put(t4);
takss.put(t5);
System.out.println(takss);
for (int i = 0; i< 5;i++){
System.out.println(takss.take());
}
PriorityQueue 排序隊列
PriorityQueue<String> q = new PriorityQueue<>();
q.add("a");
q.add("z");
q.add("k");
q.add("f");
q.add("q");
int size = q.size();
for(int i = 0; i < size; i++){
System.out.println(q.poll());
}
SynchronousQueue 進行數據傳遞。
不能add,會報錯。如果沒有put ,則 take 會一直阻塞。size 永遠爲0。
如果沒有 take,只有put,也會一直阻塞。
即只有 put 、take 都存在的情況下,纔不會進行阻塞。
BlockingQueue<String> strs = new SynchronousQueue<>();
new Thread(()->{
try {
System.out.println(strs.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
strs.put("aaaa");
System.out.println(strs.size());
LinkedTransferQueue 和SynchronousQueue 類似
不過SynchronousQueue 是一個的數據交換。 LinkedTransferQueue 是多個的。
LinkedTransferQueue<String> strings = new LinkedTransferQueue<>();
new Thread(()->{
try {
System.out.println(strings.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
strings.transfer("cccccccccc");
strings.transfer("aaaa");
System.out.println("11111111");
總結:
Vector<T>、Hashtable<K,V> ,自帶加鎖(synchronized),線程安全
Collections.synchronizedMap 是HashMap的升級。鎖的粒度變小了,而 hashtable 的鎖是在 方法上面的.
ConcurrentHashMap 插入的效率低於 hashtable、hashmap 。 讀的效率高於 hashtable、hashmap。
CopyOnWriteArrayList 寫時加鎖,讀時不加鎖。
BlockingQueue 無界阻塞隊列
ArrayBlockingQueue 有界阻塞隊列,同 BlockingQueue 差不多,不過這個是有界的。
BlockingQueue : put、take. 線程阻塞.
DelayQueue 實現自 PriorityQueue
PriorityQueue 排序隊列
SynchronousQueue 進行數據傳遞。即只有 put 、take 都存在的情況下,纔不會進行阻塞。
LinkedTransferQueue 和SynchronousQueue 類似.不過SynchronousQueue 是一個的數據交換。 LinkedTransferQueue 是多個的。