第0章:簡介
第1章:阻塞隊列BlockingQueue
第0節:札記
* BlockingQueue是一種特殊的Queue,若BlockingQueue是空的,
* 從BlockingQueue取東西的操作將會被阻斷進入等待狀態直到BlocingkQueue進了新貨纔會被喚醒。
* 同樣,如果BlockingQueue是滿的任何試圖往裏存東西的操作也會被阻斷進入等待狀態,
* 直到BlockingQueue裏有新的空間纔會被喚醒繼續操作。
* BlockingQueue提供的方法主要有:
* add(anObject): 把anObject加到BlockingQueue裏,如果BlockingQueue可以容納返回true,否則拋出IllegalStateException異常。
* offer(anObject):把anObject加到BlockingQueue裏,如果BlockingQueue可以容納返回true,否則返回false。
* put(anObject):把anObject加到BlockingQueue裏,如果BlockingQueue沒有空間,調用此方法的線程被阻斷直到BlockingQueue裏有新的空間再繼續。
* poll(time):取出BlockingQueue裏排在首位的對象,若不能立即取出可等time參數規定的時間。取不到時返回null。
* take():取出BlockingQueue裏排在首位的對象,若BlockingQueue爲空,阻斷進入等待狀態直到BlockingQueue有新的對象被加入爲止。
*
* 根據不同的需要BlockingQueue有4種具體實現:
* (1)ArrayBlockingQueue:規定大小的BlockingQueue,其構造函數必須帶一個int參數來指明其大小。其所含的對象是以FIFO(先入先出)順序排序的。
* (2)LinkedBlockingQueue:大小不定的BlockingQueue,若其構造函數帶一個規定大小的參數,生成的BlockingQueue有大小限制,
* 若不帶大小參數,所生成的BlockingQueue的大小由Integer.MAX_VALUE來決定。其所含的對象是以FIFO(先入先出)順序排序的。
* LinkedBlockingQueue和ArrayBlockingQueue比較起來,它們背後所用的數據結構不一樣,
* 導致LinkedBlockingQueue的數據吞吐量要大於ArrayBlockingQueue,但在線程數量很大時其性能的可預見性低於ArrayBlockingQueue。
* (3)PriorityBlockingQueue:類似於LinkedBlockingQueue,但其所含對象的排序不是FIFO,而是依據對象的自然排序順序或者是構造函數所帶的Comparator決定的順序。
* (4)SynchronousQueue:特殊的BlockingQueue,對其的操作必須是放和取交替完成的。
第1節:實例
package com.mcc.core.test.thread;
import com.mcc.core.concurrent.ExecutorServiceUtils;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicInteger;
/**
* BlockingQueue的生產消費實例
*
* @author <a href="mailto:[email protected]">menergy</a>
* DateTime: 13-12-30 下午2:05
*/
public class BlockingQueueTest {
public static void main(String args[]){
//原子計數器
final AtomicInteger productNum = new AtomicInteger(0);
//阻塞隊列
final BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<String>(3);
//final BlockingQueue<String> blockingQueue = new LinkedBlockingQueue<String>();
//final BlockingQueue<String> blockingQueue = new PriorityBlockingQueue<String>();
//final BlockingQueue<String> blockingQueue = new SynchronousQueue<String>();
ExecutorService executorService = ExecutorServiceUtils.getExecutor("test", 2);
//生產
executorService.execute(new Runnable() {
@Override
public void run() {
try {
while (true) {
// 生產產品
productNum.getAndIncrement();
blockingQueue.put("產品--" + productNum.get());
System.out.println("生產產品:"+ productNum.get() + ",阻塞隊列:"+ blockingQueue.toString());
// 休眠300ms
Thread.sleep(300);
}
} catch (InterruptedException ex) {
}
}
});
//消費
executorService.execute(new Runnable(){
@Override
public void run() {
try {
while (true) {
// 消費產品
String product = blockingQueue.take();
System.out.println("消費產品:"+ product + ",阻塞隊列:"+ blockingQueue.toString());
// 休眠1000ms
Thread.sleep(1000);
}
} catch (InterruptedException ex) {
}
}
});
// 程序運行5s後,所有任務停止
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
}
executorService.shutdownNow();
System.out.println("main thread finished");
}
}
第2章:監視器Condition
第0節:札記
第1節:實例
package com.mcc.core.test.thread;
import com.mcc.core.concurrent.ExecutorServiceUtils;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* Condition實現生產消費實例
*
* Lock 替代了 synchronized 方法和語句的使用,Condition 替代了 Object 監視器方法的使用。
*
* @author <a href="mailto:[email protected]">menergy</a>
* DateTime: 13-12-31 上午10:12
*/
public class ConditionTest {
public static void main(String args[]){
final Lock lock = new ReentrantLock();
final Condition produced = lock.newCondition();
final Condition consumed = lock.newCondition();
//資源持有開關,假設產品架最多隻能放3個
final AtomicInteger productNum = new AtomicInteger(0);
ExecutorService executorService = ExecutorServiceUtils.getExecutor("test", 10);
//5個生產者
for(int i = 0; i < 5; i++){
//生產
final int produceId = i;
executorService.execute(new Runnable() {
@Override
public void run() {
lock.lock();
try {
if (productNum.get() == 3){
System.out.println("產品架滿,生產者" + produceId + "等待,產品數量:" + productNum.get());
//放棄鎖,進入睡眠,等待消費者
consumed.await();
}
System.out.println("生產者" + produceId + "開始生產產品,產品數量:" + productNum.get());
// 生產產品
TimeUnit.MILLISECONDS.sleep(500);
productNum.getAndIncrement();
System.out.println("生產者" + produceId + "生產完,產品數量:" + productNum.get());
//喚醒某個等待的生產線程
produced.signal();
//喚醒所有等待的生產線程
//produced.signalAll();
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
});
}
//5個消費者
for(int i = 0; i < 5; i++){
//消費
final int consumeId = i;
executorService.execute(new Runnable(){
@Override
public void run() {
lock.lock();
try {
if (productNum.get() == 0){
System.out.println("產品架空,消費者" + consumeId + "等待,產品數量:" + productNum.get());
//放棄鎖,進入睡眠,等待生產者
produced.await();
}
System.out.println("消費者" + consumeId + "開始消費產品,產品數量:" + productNum.get());
// 生產產品
TimeUnit.MILLISECONDS.sleep(300);
productNum.getAndDecrement();
System.out.println("消費者" + consumeId + "消費完,產品數量:" + productNum.get());
//喚醒某個等待的消費線程
consumed.signal();
//喚醒所有等待的消費線程
//consumed.signalAll();
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
});
}
}
}
第3章:線程計數器CountDownLacth
第0節:札記
* CountDownLatch 是一個通用同步工具,它有很多用途。
* 將計數 1 初始化的 CountDownLatch 用作一個簡單的開/關鎖存器,或入口:
* 在通過調用 countDown() 的線程打開入口前,所有調用 await 的線程都一直在入口處等待。
* 用 N 初始化的 CountDownLatch 可以使一個線程在 N 個線程完成某項操作之前一直等待,
* 或者使其在某項操作完成 N 次之前一直等待。
第1節:實例
package com.mcc.core.test.thread;import com.mcc.core.concurrent.ExecutorServiceUtils;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
/**
* CountDownLacth線程計數器使用實例
*
* @author <a href="mailto:[email protected]">menergy</a>
* DateTime: 13-12-27 下午2:39
*/
public class CountDownLacthTest {
public static void main(String args[]){
// 實例化線程計數器
int size = 3;
final CountDownLatch lacth = new CountDownLatch(size);
Executor executor = ExecutorServiceUtils.getExecutor("test",size);
for(int i = 0; i < 3; i++){
final int threadNum = i;
executor.execute(new Runnable(){
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread running:" + threadNum);
lacth.countDown();
}
});
}
// 主線程等待子線程,一直到程序計數器爲0
try {
lacth.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("main trhread finished !");
}
}
第4章:線程計數器(柵欄)CyclicBarrier
第0節:札記
* CyclicBarrier類似於CountDownLatch也是個計數器,
* 不同的是CyclicBarrier數的是調用了CyclicBarrier.await()進入等待的線程數,
* 當線程數達到了CyclicBarrier初始時規定的數目時,所有進入等待狀態的線程被喚醒並繼續。
* CyclicBarrier就象它名字的意思一樣,可看成是個障礙,
* 所有的線程必須到齊後才能一起通過這個障礙。
* CyclicBarrier初始時還可帶一個Runnable的參數,
* 此Runnable任務在CyclicBarrier的數目達到後,所有其它線程被喚醒前被執行。
第1節:實例
package com.mcc.core.test.thread;import com.mcc.core.concurrent.ExecutorServiceUtils;
import java.util.concurrent.*;
/**
* CyclicBarrierTest線程計數器使用實例
*
* @author <a href="mailto:[email protected]">menergy</a>
* DateTime: 13-12-30 上午10:39
*/
public class CyclicBarrierTest {
public static void main(String args[]){
// 實例化線程計數器
int size = 3;
final CyclicBarrier barrier = new CyclicBarrier(size,new Runnable(){
@Override
public void run() {
System.out.println("barrier thread running !");
}
});
Executor executor = ExecutorServiceUtils.getExecutor("test", size);
//這裏測試7個線程
for(int i = 0; i < size * 2 + 1; i++){
final int threadNum = i;
executor.execute(new Runnable(){
@Override
public void run() {
try {
System.out.println("thead wait:" + threadNum);
//線程等待,barrier初始化爲3,所以要等齊三個線程抵達柵欄時才能一起通過柵欄,前6個線程需分兩批3個通過,第7個通不過,除非到達超時
//barrier.await();
try {
barrier.await(5000, TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
System.out.println("thread timeout:"+ threadNum);
}
//線程通過柵欄
System.out.println("thread running:" + threadNum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
});
}
// 主線程等待子線程,一直到程序計數器爲0
System.out.println("main trhread finished !");
}
}
第5章:延遲隊列DelayQueue
第0節:札記
* DelayQueue是一個無界的BlockingQueue,用於放置實現了Delayed接口的對象,其中的對象只能在其到期時才能從隊列中取走。* 這種隊列是有序的,即隊頭對象的延遲到期時間最長。注意:不能將null元素放置到這種隊列中。
* Delayed接口的實現必須定義一個 compareTo 方法,該方法提供與此接口的 getDelay 方法一致的排序。
第1節:實例
package com.mcc.core.test.thread;import com.mcc.core.concurrent.ExecutorServiceUtils;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* DelayQueue實例測試
*
* @author <a href="mailto:[email protected]">menergy</a>
* DateTime: 13-12-31 下午3:20
*/
public class DelayQueueTest {
static class DelayTask implements Runnable,Delayed{
//任務名
private String name;
//時間
private long time = 0;
public DelayTask(String name,long delay) {
this.name = name;
time = System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(delay, TimeUnit.MILLISECONDS);
}
@Override
public long getDelay(TimeUnit unit) {
return unit.convert(time - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
}
@Override
public int compareTo(Delayed o) {
long result = ((DelayTask) o).getTime() - this.getTime();
if (result < 0) {
return 1;
}
if (result > 0) {
return -1;
}
return 0;
}
@Override
public void run() {
System.out.println("產品被消費:" + this.toString());
}
public String toString() {
return "{name:" + this.getName() + ",延時:" + this.getDelay(TimeUnit.MILLISECONDS) + "}";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
}
public static void main(String args[]){
//原子計數器
final AtomicInteger productNum = new AtomicInteger(0);
final DelayQueue<DelayTask> delayQueue = new DelayQueue<DelayTask>();
ExecutorService executorService = ExecutorServiceUtils.getExecutor("test", 2);
//生產
executorService.execute(new Runnable() {
@Override
public void run() {
long[] times = new long[]{3000,1000,5000,500,2000,5000};
try {
for(int i = 0; i < 5; i++) {
// 生產產品
productNum.getAndIncrement();
delayQueue.add(new DelayTask("產品-" + productNum.get(), times[i]));
// 休眠300ms
Thread.sleep(300);
System.out.println("生產產品:" + productNum.get() + ",無界阻塞隊列:" + delayQueue.toString());
}
} catch (InterruptedException ex) {
}
}
});
//消費
executorService.execute(new Runnable(){
@Override
public void run() {
try {
while (true) {
// 獲取延時任務
DelayTask delayTask = delayQueue.take();
//消費產品
delayTask.run();
// 休眠1000ms
Thread.sleep(1000);
System.out.println("消費完產品:"+ delayTask.getName() + ",無界阻塞隊列:"+ delayQueue.toString());
}
} catch (InterruptedException ex) {
}
}
});
// 程序運行5s後,所有任務停止
try {
Thread.sleep(30000);
} catch (InterruptedException e) {
}
executorService.shutdownNow();
System.out.println("main thread finished");
}
}
第6章:線程間通信Exchanger
第0節:札記
* Exchanger讓兩個線程可以互換信息。
* 每個線程將條目上的某個方法呈現給 exchange 方法,與夥伴線程進行匹配,並且在返回時接收其夥伴的對象。
* Exchanger 可能被視爲 SynchronousQueue 的雙向形式。Exchanger 可能在應用程序(比如遺傳算法和管道設計)中很有用。
第1節:實例
package com.mcc.core.test.thread;import com.mcc.core.concurrent.ExecutorServiceUtils;
import java.util.concurrent.Exchanger;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Exchanger實例測試,實例以字符串接龍方式呈現
*
*
* @author <a href="mailto:[email protected]">menergy</a>
* DateTime: 13-12-30 下午4:58
*/
public class ExchangerTest {
public static void main(String args[]){
//原子計數器1
final AtomicInteger productNum1 = new AtomicInteger(0);
//原子計數器2
final AtomicInteger productNum2 = new AtomicInteger(0);
//資源持有開關,假設只有一個資源
final AtomicBoolean atomicBoolean = new AtomicBoolean(false);
// 初始化一個Exchanger,交換的信息以字符串呈現
final Exchanger<String> exchanger = new Exchanger<String>();
//交換的信息
final String[] info = {""};
ExecutorService executorService = ExecutorServiceUtils.getExecutor("test", 2);
//甲線程
executorService.execute(new Runnable() {
@Override
public void run() {
try {
while (true) {
if(!atomicBoolean.compareAndSet(false,true)){
// 處理消息
TimeUnit.MILLISECONDS.sleep(200);
productNum1.getAndIncrement();
System.out.println("甲" + productNum1.get() + "處理完成,等待乙");
//處理完,等待乙交換
info[0] = info[0] + "甲" + productNum1.get() + "--";
exchanger.exchange(info[0]);
System.out.println("甲" + productNum1.get() + "處理完成,交換給乙,信息:" + info[0]);
atomicBoolean.compareAndSet(true, false);
}else{
System.out.println("甲不處理,等待乙");
//處理完,等待乙交換
info[0] = exchanger.exchange(info[0]);
System.out.println("甲不處理,和乙交換,信息:" + info[0]);
}
}
} catch (InterruptedException ex) {
}
}
});
//乙線程
executorService.execute(new Runnable(){
@Override
public void run() {
try {
while (true) {
if(!atomicBoolean.compareAndSet(false,true)){
// 處理消息
TimeUnit.MILLISECONDS.sleep(500);
productNum2.getAndIncrement();
System.out.println("乙" + productNum2.get() + "處理完成,等待甲");
//處理完,等待乙交換
info[0] = info[0] + "乙" + productNum2.get() + "--";
exchanger.exchange(info[0]);
System.out.println("乙" + productNum2.get() + "處理完成,交換給甲,信息:" + info[0]);
atomicBoolean.compareAndSet(true, false);
}else{
System.out.println("乙不處理,等待甲");
//處理完,等待乙交換
info[0] = exchanger.exchange(info[0]);
System.out.println("乙不處理,和甲交換,信息:" + info[0]);
}
}
} catch (InterruptedException ex) {
}
}
});
// 程序運行10s後,所有任務停止
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
}
executorService.shutdownNow();
System.out.println("main thread finished");
}
}
第7章:線程間調用Join
第0節:札記
第1節:實例
package com.mcc.core.test.thread;import com.mcc.core.concurrent.ExecutorServiceUtils;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
/**
* 一個線程等待另一個線程測試
*
*
* @author <a href="mailto:[email protected]">menergy</a>
* DateTime: 13-12-31 下午1:09
*/
public class JoinTest {
/**
* A線程
*/
static class AThread implements Runnable{
@Override
public void run() {
try {
System.out.println(" A線程開始處理");
TimeUnit.MILLISECONDS.sleep(2000);
System.out.println(" A線程處理完成");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* B線程
*/
static class BThread implements Runnable{
private Thread aThread;
public BThread(Thread aThread) {
this.aThread = aThread;
}
@Override
public void run() {
try {
System.out.println("B線程開始處理");
TimeUnit.MILLISECONDS.sleep(1000);
System.out.println("B線程處理到一半,啓動A線程,等待A線程處理");
//啓動A線程
aThread.start();
//等待A線程完成
aThread.join();
System.out.println("B線程繼續處理另一半");
TimeUnit.MILLISECONDS.sleep(1000);
System.out.println("B線程處理完成");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String args[]){
Thread aThread = new Thread(new AThread());
ExecutorService executorService = ExecutorServiceUtils.getExecutor("test", 2);
//啓動B線程
executorService.execute(new BThread(aThread));
}
}
第8章:讀寫鎖ReadWriteLock
第0節:札記
* ReadWriteLock 維護了一對相關的鎖,一個用於只讀操作,另一個用於寫入操作。* 只要沒有 writer,讀取鎖可以由多個 reader 線程同時保持。寫入鎖是獨佔的。
* 從理論上講,與互斥鎖相比,使用讀-寫鎖所允許的併發性增強將帶來更大的性能提高。
* 與互斥鎖相比,使用讀-寫鎖能否提升性能則取決於讀寫操作期間讀取數據相對於修改數據的頻率,
* 以及數據的爭用——即在同一時間試圖對該數據執行讀取或寫入操作的線程數。
* 此鎖最多支持 65535 個遞歸寫入鎖和 65535 個讀取鎖。試圖超出這些限制將導致鎖方法拋出 Error。
第1節:實例
package com.mcc.core.test.thread;import com.mcc.core.concurrent.ExecutorServiceUtils;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* ReadWriteLock讀寫鎖測試
*
*
* @author <a href="mailto:[email protected]">menergy</a>
* DateTime: 13-12-31 上午11:26
*/
public class ReadWriteLockTest {
public static void main(String args[]){
ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
final Lock readLock = readWriteLock.readLock();
final Lock writeLock = readWriteLock.writeLock();
ExecutorService executorServiceMain = ExecutorServiceUtils.getExecutor("Main", 10);
final ExecutorService executorService = ExecutorServiceUtils.getExecutor("test", 10);
executorServiceMain.execute(new Runnable() {
@Override
public void run() {
//10個讀線程
for(int i = 0; i < 10; i++){
final int readId = i;
executorService.execute(new Runnable() {
@Override
public void run() {
readLock.lock();
try {
System.out.println("線程" + readId +"開始讀");
// 讀處理
TimeUnit.MILLISECONDS.sleep(300);
System.out.println("線程" + readId +"讀完成");
}catch (InterruptedException e){
e.printStackTrace();
}finally {
readLock.unlock();
}
}
});
}
}
});
executorServiceMain.execute(new Runnable() {
@Override
public void run() {
//2個寫線程
for(int i = 0; i < 2; i++){
//消費
final int writeId = i;
executorService.execute(new Runnable(){
@Override
public void run() {
writeLock.lock();
try {
System.out.println("線程" + writeId +"開始寫");
// 讀處理
TimeUnit.MILLISECONDS.sleep(500);
System.out.println("線程" + writeId + "寫完成");
}catch (InterruptedException e){
e.printStackTrace();
}finally {
writeLock.unlock();
}
}
});
}
}
});
}
}
第9章:信號量Semaphore
第0節:札記
* 獲得一項前,每個線程必須從信號量獲取許可,從而保證可以使用該項。* 該線程結束後,將項返回到池中並將許可返回到該信號量,從而允許其他線程獲取該項。
* 注意,調用 acquire() 時無法保持同步鎖,因爲這會阻止將項返回到池中。
* 信號量封裝所需的同步,以限制對池的訪問,這同維持該池本身一致性所需的同步是分開的。
第1節:實例
package com.mcc.core.test.thread;import com.mcc.core.concurrent.ExecutorServiceUtils;
import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 信號量線程控制實例測試
*
* @author <a href="mailto:[email protected]">menergy</a>
* DateTime: 13-12-30 下午3:47
*/
public class SemaphoreTest {
public static void main(String args[]){
//信號量,初始化2
final Semaphore semaphore = new Semaphore(2);
//同步鎖
final Lock lock = new ReentrantLock();
//資源池
final ArrayList<String> resourcePool = new ArrayList<String>();
for (int i = 0; i < 8; i++) {
resourcePool.add("Resource " + i);
}
ExecutorService executorService = ExecutorServiceUtils.getExecutor("test", 5);
//啓動5個線程執行5個任務
for (int i = 0; i < 5; i++) {
executorService.submit(new Runnable(){
@Override
public void run() {
try {
//獲取通行證,只有得到通行證後才能得到資源
semaphore.acquire();
System.out.println("獲取通行證,可用數量:" + semaphore.availablePermits());
//取走資源,需同步
lock.lock();
String resource = resourcePool.remove(0);
System.out.println("取走資源:" + resource + "資源池:" + resourcePool.toString());
lock.unlock();
//使用資源
System.out.println("使用資源:" + resource + "資源池:" + resourcePool.toString());
TimeUnit.MILLISECONDS.sleep(2000);
//歸還資源,需同步
lock.lock();
resourcePool.add(resource);
System.out.println("歸還資源:" + resource + "資源池:" + resourcePool.toString());
lock.unlock();
//釋放許可證,可以給其它線程使用
semaphore.release();
System.out.println("釋放通行證,可用數量:" + semaphore.availablePermits());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
executorService.shutdown();
}
}