上面介紹了併發編程中的柵欄等JAVA併發之多線程基礎(4) 。經過惟一的一個終點線來幫助肯定線程是多晚開始執行下一次操做。java
提供了一個比較底層的線程掛起操做。有點相似於
suspend()
方法,可是這個方法不建議使用。編程
park()
使得當前線程掛起。裏面也是調用了Unsafe
類進行底層操做。public static void park() {
UNSAFE.park(false, 0L);
}
複製代碼
2.unpark(Thread thread)
是將當前線程繼續往下執行。數組
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
複製代碼
LockSupport
上面的兩個方法不像suspend()
與resume()
方法。在這兩個方法中resume()
必須在suspend()
以前執行。不然線程就會被永遠的掛起,形成死鎖。而LockSupport
中的unpark
能夠在park
以前。安全
package com.montos.lock;
import java.util.concurrent.locks.LockSupport;
public class LockSupportDemo {
public static Object obj = new Object();
static ChangeObjectThread t1 = new ChangeObjectThread("t1");
static ChangeObjectThread t2 = new ChangeObjectThread("t2");
public static class ChangeObjectThread extends Thread {
public ChangeObjectThread(String name) {
super(name);
}
@Override
public void run() {
synchronized (obj) {
System.out.println("in " + getName());
LockSupport.park();
}
}
}
public static void main(String[] args) throws InterruptedException {
t1.start();
Thread.sleep(100);
t2.start();
LockSupport.unpark(t1);
LockSupport.unpark(t2);
t1.join();
t2.join();
}
}
複製代碼
經過上面的一個小的Demo能夠看出
LockSupport
的使用方法,在使用方法中也是很簡單的。以上篇幅就對JDK內的併發類進行了講解。大多數用到了CAS無鎖操做。避免線程阻塞的狀況發生。多線程
在平常的業務處理中,集合類是咱們使用最多的一個操做。對應的也就是三大類:
Map
、Set
以及最後的List
。在平常併發量小的狀況下,也許咱們會這麼使用對應的集合操做:併發
//Map集合
private static final Map<String,String> map = Collections.synchronizedMap(new HashMap<String,String>());
//List集合
private static final List<String> list = Collections.synchronizedList(new ArrayList<String>());
//Set集合
private static final Set<String> set = Collections.synchronizedSet(new HashSet<>());
複製代碼
上面的使用了Collections
中的同步方法進行包裝。深刻了解裏面的調用過程,也就是各個方法上面加上了synchronized
進行限制。從而達到最後線程安全的目的。若是併發量很大的話,是很會影響性能的,畢竟它使得對應的讀和寫操做都變成了串行。因而有了下面的幾種線程安全類:ide
ConcurrentHashMap
高性能併發的Map。裏面的操做是跟HashMap
是同樣的。可是裏面多了一種結構:Segment
(段)。static class Segment<K,V> extends ReentrantLock implements Serializable {
private static final long serialVersionUID = 2249069246763182397L;
final float loadFactor;
Segment(float lf) { this.loadFactor = lf; }
}
複製代碼
能夠看出他是繼承了重入鎖進行實現的。爲何會有段的概念在這裏面呢?爲了更好的支持高併發而設計的一個概念。每一個元素在特定的段中,將整個集合分紅許多段進行管理,而高併發的時候,只要對每一個段進行控制就能起到一個同步的做用。高併發
再統計數量大小的時候,ConcurrentHashMap
中利用了CounterCell
結構體進行統計。每個段中存儲了該段的大小,而後再循環求和出當前的數組大小狀況。post
final long sumCount() {
CounterCell[] as = counterCells; CounterCell a;
long sum = baseCount;
if (as != null) {
for (int i = 0; i < as.length; ++i) {
if ((a = as[i]) != null)
sum += a.value;
}
}
return sum;
}
複製代碼
BlockingQueue
是一個很是好的多線程共享數據的容器。當隊列爲空的時候,讀線程就會進行等待,相反,若是當前隊列已滿,那麼寫線程就會等待。看到對應的實現類中都會有下面三個成員變量:/** Main lock guarding all access */
final ReentrantLock lock; //加鎖進行控制訪問
/** Condition for waiting takes */
private final Condition notEmpty;//元素獲取的時候,進行判斷是否爲空進行阻塞
/** Condition for waiting puts */
private final Condition notFull;//元素加入的時候,判斷隊列是否已滿
複製代碼
這上面的三個變量就控制整個阻塞隊列的訪問以及元素的增長。從根本上來講他並不像上面介紹的ConcurrentHashMap
訪問性能高,可是咱們須要的是他的多線程訪問共有數據的能力。性能
阻塞隊列的元素獲取方法
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();//獲取可中斷鎖
try {
while (count == 0)
notEmpty.await();//隊列爲空,進行讀線程等待
return dequeue();
} finally {
lock.unlock();
}
}
複製代碼
private E dequeue() {
// assert lock.getHoldCount() == 1;
// assert items[takeIndex] != null;
final Object[] items = this.items;
@SuppressWarnings("unchecked")
E x = (E) items[takeIndex];
items[takeIndex] = null;
if (++takeIndex == items.length)
takeIndex = 0;
count--;
if (itrs != null)
itrs.elementDequeued();
notFull.signal();//通知寫線程進行寫入操做
return x;
}
複製代碼
阻塞隊列的元素增長方法
public void put(E e) throws InterruptedException {
checkNotNull(e);//判斷元素是否爲空
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();//獲取可中斷鎖
try {
while (count == items.length)
notFull.await();//隊列滿時,進行寫線程等待
enqueue(e);
} finally {
lock.unlock();
}
}
複製代碼
private void enqueue(E x) {
// assert lock.getHoldCount() == 1;
// assert items[putIndex] == null;
final Object[] items = this.items;
items[putIndex] = x;
if (++putIndex == items.length)
putIndex = 0;
count++;
notEmpty.signal();//通知讀線程進行操做
}
複製代碼
ConcurrentLinkedQueue
上面談及到的是多線程共享數據的容器,而這個是高併發狀況下使用的。裏面大量的使用了無鎖的操做以及鎖自旋,能夠很好的保證性能問題,有興趣的小夥伴能夠去了解下。以上談及到的就是JDK中關於併發的一些簡單介紹。在我介紹完成以後,我會對底層的源碼進一步講解,有興趣的小夥伴能夠關注我~