1. 程式人生 > >java執行緒的全面講解

java執行緒的全面講解

執行緒的使用在開發中可以說是無處不在,場景特別多;當然也是很難控制的。當然你要玩的好,也是很好的。

簡單的講,執行緒本質上不能加快程式的執行(當然多cpu的機器例外了),只不過優化時間排程而已,在我們看來整體上快了點;但搞不好由於線上程間的切換消耗太多精力導致整個程式執行效率降低也很有可能,所以為何多執行緒的情況下就要不斷嘗試,找到最優執行緒數,也就是這個道理了。不過多執行緒執行有一個明顯的好處啦(不管程式是變快了還是變慢了),那就是對於使用者來說,減少對使用者的等待時間,不然單執行緒跑任務,使用者可能面對的就是一個時刻“卡死”的程式了(特別在介面上的處理)。

下面就用程式碼來描述執行緒的一些基本知識,這裡描述的比較多,但基本能覆蓋工作當中的應用了。

package com.wxshi.thread;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.Exchanger;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 執行緒的介紹,這裡做個筆記 下面會從簡單到高階介紹執行緒
 *
 * @author wxshi
 *
 */
public class TraditionalThread {

	// -----------演示執行緒的建立方法,及內部方法體的呼叫原理-----------

	/**
	 * 第一種建立執行緒 :public class ChildThread extends Thread(){}
	 * 通過Thread子類覆蓋Thread的run()方法,以實現執行緒執行自己的業務
	 */
	public void thread_init1() {
		Thread thread = new Thread() {
			@Override
			public void run() {
				while (true) {
					try {
						Thread.sleep(1000);
						System.out.println(Thread.currentThread().getName());
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		};
		thread.start();
	}

	/**
	 * 第二種方法建立執行緒:Thread thread = new Thread(Runnable runnable);
	 * 將執行緒執行的run()方法宿主到Runnable物件,以實現面向物件思想。
	 * Thread在構造的時候會將Runnable內的run()方法作為自己的目標,在啟動時會執行
	 *
	 */
	public void thread_init2() {
		Thread thread = new Thread(new Runnable() {

			@Override
			public void run() {
				try {
					Thread.sleep(1000);
					System.out.println(Thread.currentThread().getName());
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		});

		thread.start();
	}

	/**
	 * 注意:ruannable()宿主裡面的run方法會賦給Thread(父類),所以上面兩種方法同時出現時
	 * 只會呼叫子類執行緒實現方式的run方法,因為覆蓋了Thread(父類)的run方法,管你怎麼傳,都會被覆蓋。
	 * */

	// -----------演示執行緒定時任務,quartz的使用-----------

	/**
	 * 執行緒之定時任務 具體定時需求更多的可以呼叫quartz時間api進行配置定時,這裡不做多介紹
	 *
	 **/
	public void thread_traditionTimerTask() {
		Timer task = new Timer();

		// 10s以後啟動定時任務,只執行一次
		task.schedule(new TimerTask() {
			@Override
			public void run() {
				System.out.println("task start");
			}
		}, 10000);

		// 10s以後啟動定時任務,以後每5s執行一次
		task.schedule(new TimerTask() {
			@Override
			public void run() {
				System.out.println("task start");
			}
		}, 10000, 5000);
	}

	// -----------演示執行緒互斥,鎖的使用-----------

	OutPrint out = new OutPrint();

	/**
	 * 執行緒間的互斥,下面的方法僅僅代表在被多執行緒呼叫時,會實現執行緒間的互斥 多個執行緒使用同一把鎖會實現方法的同步,若使用的不是同一把鎖,就不能實現同步
	 *
	 */
	public void thread_sync1() {

		// 10個執行緒執行緒呼叫列印方法,保證每個執行緒都列印完整的字串
		for (int i = 0; i < 10; i++) {
			new Thread(new Runnable() {
				@Override
				public void run() {
					try {
						Thread.sleep(500);
						out.print1("shiwenxue");
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}).start();
		}
	}

	/**
	 * 內部類。保證程式完整列印str字串
	 */
	static class OutPrint {

		String lock = ""; // 這裡定義只當鎖,沒有其他作用。

		/**
		 * 方法體 只鎖住部分程式碼段,該保護的程式碼被保護,這部分程式碼只能一次被一個執行緒共享 此處用的鎖是該物件
		 * 適用於一個方法部分程式碼共享,部分程式碼受保護
		 *
		 * @param str
		 */
		public void print1(String str) {

			// 鎖住方法主體,使得沒車只供一個縣執行緒呼叫
			// this可以是一個公共變數,代表鎖栓,不一定是當前物件,這裡用當前物件當鎖栓
			// synchronized (lock) lock在方法體外定義的一個物件,也可以充當鎖栓。

			synchronized (this) {
				for (char c : str.toCharArray()) {
					System.out.print(c);
				}
				System.out.println();
			}
		}

		/**
		 * 鎖住整個方法,該保護的程式碼被保護,這部分程式碼只能一次被一個執行緒共享 此處用的鎖是該物件
		 * 注意:由於此方法的鎖也是該物件,與print1是同一個鎖,因此這兩個方法本身也互斥(鎖在誰手上,誰就有權利呼叫) 如果,print1
		 * 用的鎖是lock(定義的string),而print2用的是this,則不互斥
		 *
		 * @param str
		 */
		public synchronized void print2(String str) {
			for (char c : str.toCharArray()) {
				System.out.println(c);
			}
		}

		/**
		 * 若新增靜態方法,並且想與print1,print2兩個方法同步,所有方法必須使用OutPrint.class物件作為鎖
		 * 因為靜態方法屬於類本身,而類本身位元組碼(OutPrint.class)就是靜態方法所屬物件。
		 *
		 * @param str
		 */
		public static synchronized void print3(String str) {
			for (char c : str.toCharArray()) {
				System.out.println(c);
			}
		}
	}

	/**
	 * java5中提供了另外一種鎖Lock,下面簡單說明一下(這種鎖更加面向物件)
	 */
	class output2 {

		// 定義鎖
		Lock lock = new ReentrantLock();

		/**
		 * 下面的方法會上鎖
		 *
		 * @param str
		 */
		public void print1(String str) {
			lock.lock(); // 給核心程式碼段上鎖
			try {
				for (char c : str.toCharArray()) {
					System.out.println(c);
				}
			} finally { // 若核心程式碼段執行異常了 ,當然要釋放鎖
				lock.unlock();// 釋放鎖,
			}
		}
	}

	/**
	 * 以一個自定義的快取器來演示讀寫鎖:
	 * 讀鎖:允許多個執行緒同時讀取,但不能寫;
	 * 寫鎖:只允許當前執行緒寫,不允許其他操作
	 */
	class MyCache {

		// 快取map
		private Map<String, Object> myCache = new HashMap<String, Object>();

		// 讀寫鎖
		private ReadWriteLock rwLock = new ReentrantReadWriteLock();

		// 獲取資料,這裡允許多人同時獲取,但在寫時只能一個人操作
		public Object getData(String key) {
			rwLock.readLock().lock(); // 上讀鎖,允許多人讀,但此時不允許寫
			Object value = null;
			try {
				value = myCache.get(key);
				// 若快取中沒有資料,就從資料庫或其他地方獲取,並加入快取
				// 但此時釋放讀鎖,加寫鎖,保證只有一個人能操作且只操作一次
				if (null == value) {
					rwLock.readLock().unlock();// 釋放讀鎖
					rwLock.writeLock().lock();// 加寫鎖
					try {
						if (null == value) { // 這裡再判斷一次是防止其他執行緒也走到這一步再次獲取
							value = ""; // 從其他地方獲取
						}
					} finally {
						rwLock.writeLock().unlock();// 獲取了資料之後,釋放寫鎖,再加讀鎖
					}
				}
			} finally {
				rwLock.readLock().unlock();// 釋放讀鎖
			}
			return value;
		}
	}

	// -----------演示執行緒間通訊-----------

	/**
	 * 執行緒間通訊,這裡以一個示例演示:子執行緒執行10次,主執行緒執行20次,交替執行,各自執行100次
	 */
	public void thread_communication() {

		final Business business = new Business();

		// 定義子執行緒
		new Thread(new Runnable() {
			@Override
			public void run() {
				for (int i = 0; i < 100; i++) {
					business.subBusiness(); // 子執行緒業務
				}

			}
		}).start();

		// 方法體本身作為主執行緒
		for (int i = 0; i < 100; i++) {
			business.mainBusiness(); // 主執行緒業務
		}
	}

	/**
	 * 內部類,裡面包含主執行緒與子執行緒業務實現, 這樣容易管理任務,實現互斥與通訊
	 *
	 * @author wxshi
	 *
	 */
	class Business {

		// 喚醒標誌,第一次執行為子執行緒,保證交替執行
		private boolean isTimeForSub = true;

		/**
		 * 子執行緒業務,執行10次 加鎖保證每次要麼子執行緒執行,要麼主執行緒執行
		 */
		public synchronized void subBusiness() {

			// 若為false,則子執行緒等待
			while (!isTimeForSub) {
				try {
					this.wait();// 當前執行緒等待
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}

			// 若為true,則執行程式碼
			for (int i = 0; i < 10; i++) {
				System.out.println("sub loop times: " + i);
			}

			isTimeForSub = false; // 執行完10次,喚醒標誌為false
			this.notify(); // 喚醒其他執行緒(此處為主執行緒)
		}

		/**
		 * 主執行緒業務,執行20次
		 */
		public synchronized void mainBusiness() {
			// 若為true,則主執行緒等待
			while (isTimeForSub) {
				try {
					this.wait();// 當前執行緒等待
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}

			// 若為false,則執行程式碼
			for (int i = 0; i < 20; i++) {
				System.out.println("main loop times: " + i);
			}

			isTimeForSub = true; // 執行完20次,喚醒標誌為true
			this.notify(); // 喚醒其他執行緒
		}
	}

	/**
	 * 內部類,裡面包含主執行緒與子執行緒業務實現, 這樣容易管理任務,實現互斥與通訊
	 * 此類的演示完全與上面Business一樣,只不過這裡使用Condition來實現執行緒間的通訊,取代傳統的 wait() 和 notify();
	 * 注意:Condition與Lock成對使用
	 *
	 * @author wxshi
	 *
	 */
	class Business2 {

		// 喚醒標誌,第一次執行為子執行緒,保證交替執行
		private boolean isTimeForSub = true;

		// 定義鎖
		Lock lock = new ReentrantLock();
		// 獲取Condition
		Condition condition = lock.newCondition();

		/**
		 * 子執行緒業務,執行10次 加鎖保證每次要麼子執行緒執行,要麼主執行緒執行
		 */
		public void subBusiness() {

			// 若為false,則子執行緒等待
			while (!isTimeForSub) {
				try {
					// this.wait();// 當前執行緒等待
					condition.await(); // 此處用await();代表等待
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}

			// 若為true,則執行程式碼
			for (int i = 0; i < 10; i++) {
				System.out.println("loop times: " + i);
			}

			isTimeForSub = false; // 執行完10次,喚醒標誌為false
			// this.notify(); // 喚醒其他執行緒(此處為主執行緒)
			condition.signal(); // 喚醒其他執行緒(此處為主執行緒)
		}

		/**
		 * 主執行緒業務,執行20次
		 */
		public synchronized void mainBusiness() {
			// 若為true,則主執行緒等待
			while (isTimeForSub) {
				try {
					// this.wait();// 當前執行緒等待
					condition.await(); // 此處用await();代表等待
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}

			// 若為false,則執行程式碼
			for (int i = 0; i < 20; i++) {
				System.out.println("loop times: " + i);
			}

			isTimeForSub = true; // 執行完20次,喚醒標誌為true
			// this.notify(); // 喚醒其他執行緒
			condition.signal(); // 喚醒其他執行緒(此處為主執行緒)
		}
	}

	/**
	 * 這裡演示利用Condition實現的自定義的佇列 利用Condition建立不同的物件實現不同效果的通訊,而不相互影響
	 * 下面的程式碼直接取javaAPI的程式碼,因為這裡思想確實很好,實現也簡單
	 * 這裡演示的是自定義緩衝佇列
	 */
	class MyQueue {
		final Lock lock = new ReentrantLock();
		final Condition notFull = lock.newCondition();
		final Condition notEmpty = lock.newCondition();

		// 緩衝佇列,緩衝大小偉100
		final Object[] queue = new Object[100];
		int putIndex, getIndex, count;

		// 存值
		public void put(Object x) throws InterruptedException {
			lock.lock();
			try {
				// 若緩衝區已滿,則notFull鎖等待
				while (count == queue.length) {
					notFull.await();
				}
				if (++putIndex == queue.length) {
					putIndex = 0; // 若放到最後一個區間了,則再往第一個區間放
				}
				count++; // 代表放了一個數,喚醒取值執行緒去取
				notEmpty.signal();
			} finally {
				lock.unlock();
			}
		}

		// 取值
		public Object get() throws InterruptedException {
			lock.lock();
			try {
				// 若緩衝區沒有資料滿,則notEmpty鎖等待
				while (count == 0) {
					notEmpty.await();
				}
				Object x = queue[getIndex]; // 取值
				if (++getIndex == queue.length) {
					getIndex = 0; // 若取到最後一個區間了,則再從第一個區間取
				}
				count--; // 代表取放了一個數,喚醒放值執行緒
				notFull.signal(); // 喚醒放值執行緒,可以放值了
				return x;
			} finally {
				lock.unlock();
			}
		}
	}

	// -----------演示執行緒間變數共享,而保持內部資料獨立-----------

	/**
	 * 定義一個全域性ThreadLocal物件,注意:有多少個變數需要處理,就要定義多少個ThreadLocal
	 * 即一個ThreadLocal只能管理一個變數,當然設計是你自己的問題(比如很多變數封裝成一個物件,再用一個ThreadLocal來存放物件)
	 */
	private static ThreadLocal<Integer> threadData = new ThreadLocal<Integer>();

	/**
	 * 這裡主要演示ThreadLocal類的使用, 當然我們實現執行緒變數共享又保持資料相對獨立,可以使用全域性map為每個執行緒儲存自己的那一份資料
	 * 但這裡直接使用ThreadLocal操作更加簡便,並且執行緒終止時會自動釋放記憶體,方便管理
	 */
	public void thread_dataLocal() {

		// 定義兩個執行緒,分別存放自己的隨機值,然後分別呼叫取值函式去取
		// 測試是否自己取出來的是自己存放的資料
		for (int i = 0; i < 2; i++) {
			new Thread(new Runnable() {

				@Override
				public void run() {
					int data = new Random().nextInt();
					System.out.println(Thread.currentThread().getName() + " put data: " + data);
					threadData.set(data);// 將資料交給ThreadLocal管理,內部跟map原理相似
					new GetA().get();
					new GetB().get();
				}
			}).start();
		}
	}

	/**
	 * 定義獲取執行緒的值
	 */
	static class GetA {
		public void get() {
			int data = threadData.get();// 會直接根據當前呼叫執行緒取出
			System.out.println(Thread.currentThread().getName() + " get data of A:" + data);
		}
	}

	/**
	 * 同上
	 */
	static class GetB {
		public void get() {
			int data = threadData.get();
			System.out.println(Thread.currentThread().getName() + " get data of B:" + data);
		}
	}

	/**
	 * 這裡介紹封裝多個變數資料的方法,這種設計很好,在物件內部封裝ThreadLocal
	 */
	static class MyThreadScopeData {

		// 構造方法私有化
		private MyThreadScopeData() {
		};

		// 以執行緒為key放置資料物件
		private static ThreadLocal<MyThreadScopeData> myThreadData = new ThreadLocal<MyThreadScopeData>();

		// 單個執行緒內單例,好就好在這,每個執行緒獨自單例
		public static MyThreadScopeData getInstanceForThread() {
			MyThreadScopeData scopeData = myThreadData.get();
			if (null == scopeData) {
				scopeData = new MyThreadScopeData();
				myThreadData.set(scopeData);
			}
			return scopeData;
		}
	}

	// -----------演示執行緒原子性-----------

	/**
	 * 將普通基本變數封裝成原執性,實現線上程間互斥
	 */
	public void thread_atomic() {

		// 定義原子性int變數,並初始化為0
		AtomicInteger atomicInt = new AtomicInteger(0);
		atomicInt.addAndGet(2); // +2
		atomicInt.addAndGet(-2); // -2
		atomicInt.decrementAndGet(); // -1
	}

	// -----------演示執行緒池-----------

	/**
	 * 執行緒池的使用 建立執行緒池的方法大概有下面幾種,這裡簡單介紹下
	 */
	public void thread_pool() {

		// 建立一個擁有5個執行緒的執行緒池(固定執行緒池)
		ExecutorService threadPool = Executors.newFixedThreadPool(5);

		// 建立一個緩衝執行緒池,這樣會隨著任務增多,執行緒池中執行緒會自動變更
		ExecutorService threadPool2 = Executors.newCachedThreadPool();

		// 特殊執行緒池,內部始終僅保持一個也僅有一個執行緒(死了會自動新建立一個)
		ExecutorService threadPool3 = Executors.newSingleThreadExecutor();

		// 定時任務執行緒池,會呼叫內部執行緒中的一個或幾個去定時執行任務,呼叫schedule()方法會執行任務
		ExecutorService threadPool4 = Executors.newScheduledThreadPool(6);

		// 定義10個任務
		for (int i = 0; i < 10; i++) {

			final int task = i; // 供任務內部呼叫

			// 執行任務,這裡會隨機去5個執行緒中的一個執行
			threadPool.execute(new Runnable() {
				@Override
				public void run() {
					System.out.println("第" + task + "個任務被" + Thread.currentThread().getName() + "執行");
				}
			});
		}
		threadPool.shutdown();// 執行完任務關閉執行緒池
		threadPool.shutdownNow(); // 不管有沒有執行完,立馬關閉
	}

	// -----------演示執行緒回撥任務執行結果-----------

	/**
	 * Callable 與 Future 成對使用,一個返回回撥結果,一個獲取結果 更多操作檢視API
	 */
	public void thread_callableAndFuture() {
		ExecutorService threadPool = Executors.newSingleThreadExecutor();

		// 通過執行緒submit的執行任務並返回回撥結果,結果儲存在Future中
		Future<String> future = threadPool.submit(new Callable<String>() {
			@Override
			public String call() throws Exception {
				return "call回撥返回的值";
			}
		});

		try {
			String result = future.get(); // 獲取回撥結果
			System.out.println("回撥結果:" + result);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * CompletionService 用於提交一組callable任務,其take方法用於返回一個已完成的callable任務
	 */
	public void thread_completionService() throws InterruptedException, ExecutionException {
		// 建立一個擁有5個執行緒的執行緒池(固定執行緒池,CompletionService會使用執行緒池中的執行緒去執行任務)
		ExecutorService threadPool = Executors.newFixedThreadPool(5);

		CompletionService<String> completionService = new ExecutorCompletionService<String>(threadPool);

		for (int i = 0; i < 10; i++) {
			final int task = i;

			completionService.submit(new Callable<String>() {
				@Override
				public String call() throws Exception {
					return Thread.currentThread().getName() + "回撥結果:" + task;
				}
			});
		}

		// 拿10遍結果
		for (int i = 0; i < 10; i++) {
			System.out.println(completionService.take().get());
			completionService.take().get();// 捕獲結果
		}
	}

	// -----------演示執行緒訊號燈的使用----------

	/**
	 * 執行緒之間的訊號燈的使用,可以實現一個事件在同一時刻被多少個執行緒訪問。
	 *
	 * @param args
	 */
	public void thread_semaphore() {

		// 執行緒池
		ExecutorService service = Executors.newCachedThreadPool();

		// 定義4個訊號燈
		final Semaphore semaphore = new Semaphore(4);
		// final Semaphore semaphore = new Semaphore(4,true); //true:是否保證先來先得

		// 定義10個任務,但每次只能有三個任務被同時執行(訊號燈控制)
		for (int i = 0; i < 20; i++) {
			Runnable runnable = new Runnable() {
				@Override
				public void run() {
					try {
						semaphore.acquire(); // 點亮訊號燈,代表一個坑被佔
					} catch (InterruptedException e1) {
						e1.printStackTrace();
					}
					System.out.println("執行緒" + Thread.currentThread().getName() + "進入,當前已有" + (3 - semaphore.availablePermits()) + "個併發");
					try {
						Thread.sleep((long) (Math.random() * 10000));
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println("執行緒" + Thread.currentThread().getName() + "即將離開");
					semaphore.release(); // 熄滅訊號燈,代表一個坑被釋放

					System.out.println("執行緒" + Thread.currentThread().getName() + "已離開,當前已有" + (3 - semaphore.availablePermits()) + "個併發");
				}
			};
			service.execute(runnable);
		}

		try {
			Thread.sleep((long) (Math.random() * 100000));
			service.shutdown();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

	}

	// -----------演示執行緒“同步”進行任務----------

	/**
	 * CyclicBarrier同步工具 表示要求一定數量執行緒同時到達某一狀態才繼續一起往下執行,否則就等待其他行程 中間可以設定多個狀態
	 * 如:完成各自的任務後集合吃飯,吃晚飯後又各自做自己的事
	 */
	public void thread_cyclicBarrier() {

		ExecutorService service = Executors.newCachedThreadPool();

		// 定義CyclicBarrier同步工具,表示要有5個執行緒同時到達某一狀態才繼續一起往下執行,否則就等待其他行程
		final CyclicBarrier cyclicBarrier = new CyclicBarrier(5);
		for (int i = 0; i < 5; i++) {
			Runnable runnable = new Runnable() {
				@Override
				public void run() {
					try {
						Thread.sleep((long) (Math.random() * 10000));
						System.out.println("執行緒" + Thread.currentThread().getName() + "即將到達集合地點1,當前已有" + (cyclicBarrier.getNumberWaiting() + 1) + "個已經到達,"
								+ (cyclicBarrier.getNumberWaiting() == 4 ? "都到齊了,可以走了" : "等候其他執行緒"));

						// (第一個集合點)沒到達就要等候
						cyclicBarrier.await();

						Thread.sleep((long) (Math.random() * 10000));
						System.out.println("執行緒" + Thread.currentThread().getName() + "即將到達集合地點2,當前已有" + (cyclicBarrier.getNumberWaiting() + 1) + "個已經到達,"
								+ (cyclicBarrier.getNumberWaiting() == 4 ? "都到齊了,可以走了" : "等候其他執行緒"));

						// (第二個集合點)沒到達就要等候
						cyclicBarrier.await();

						Thread.sleep((long) (Math.random() * 10000));
						System.out.println("執行緒" + Thread.currentThread().getName() + "即將到達集合地點3,當前已有" + (cyclicBarrier.getNumberWaiting() + 1) + "個已經到達,"
								+ (cyclicBarrier.getNumberWaiting() == 4 ? "都到齊了,可以走了" : "等候其他執行緒"));
						cyclicBarrier.await();
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			};
			service.execute(runnable);
		}

		service.shutdown();
		// try {
		// Thread.sleep((long)(Math.random()*10000));
		// // service.shutdown();
		// } catch (InterruptedException e) {
		// e.printStackTrace();
		// }

	}

	// -----------演示執行緒“倒計時”命令----------

	/**
	 * CountDownLatch可以實現倒計時
	 * CountDownLatch.countDown()可以將時間減1,直到0時,所有執行緒可以進行下去,否則阻塞。 下面實現裁判員跟運動員的關係
	 *
	 * @param args
	 */
	public void thread_countDownLatch() {
		ExecutorService service = Executors.newCachedThreadPool();

		// 倒計時1:初始化為1,當裁判員發動命令時,便呼叫countDown()減到0,這時促使運動員執行
		final CountDownLatch cdOrder = new CountDownLatch(1);

		// 倒計時2:初始化為3,代表三個運動員,但每個運動員執行完,計數器便減1,當所有運動員執行完,便告知裁判員已完成
		final CountDownLatch cdAnswer = new CountDownLatch(3);
		for (int i = 0; i < 3; i++) {
			Runnable runnable = new Runnable() {
				public void run() {
					try {
						Thread.sleep((long) (Math.random() * 10000));

						System.out.println("執行緒" + Thread.currentThread().getName() + "正準備接受命令");

						cdOrder.await();// 等待裁判員命令,只要計數器不為0,就一直等待

						System.out.println("執行緒" + Thread.currentThread().getName() + "已接受命令");

						Thread.sleep((long) (Math.random() * 10000));

						System.out.println("執行緒" + Thread.currentThread().getName() + "執行完");

						cdAnswer.countDown(); // 每執行完一個,裁判員計數器便減1

					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			};
			service.execute(runnable);
		}

		try {
			Thread.sleep((long) (Math.random() * 10000));

			System.out.println("執行緒" + Thread.currentThread().getName() + "準備釋出命令");

			cdOrder.countDown();// 減1,代表裁判員發動命令

			System.out.println("執行緒" + Thread.currentThread().getName() + "已傳送命令,正在等待結果");

			cdAnswer.await();// 等待計數器為0,即等待所有運動員全部執行完

			System.out.println("執行緒" + Thread.currentThread().getName() + "已收到所有響應結果");
		} catch (Exception e) {
			e.printStackTrace();
		}
		service.shutdown();
	}

	// -----------演示執行緒間的資料交換----------

	/**
	 * Exchanger可以實現執行緒間的資料交換,只要有一方資料沒到,另一方就一直等待,但同時到達時才進行資料交換
	 *
	 * @param args
	 */
	public void thread_exchanger() {

		ExecutorService service = Executors.newCachedThreadPool();

		// 交換器
		final Exchanger<String> exchanger = new Exchanger<String>();

		// 執行第一個執行緒
		service.execute(new Runnable() {
			public void run() {
				try {

					String data1 = "執行緒1的資料";
					System.out.println("執行緒" + Thread.currentThread().getName() + "正在把資料:" + data1 + " 換出去");
					Thread.sleep((long) (Math.random() * 10000));

					// 執行緒1資料到了
					String data2 = (String) exchanger.exchange(data1);

					System.out.println("執行緒" + Thread.currentThread().getName() + "換回的資料為:" + data2);
				} catch (Exception e) {

				}
			}
		});

		// 執行第二個執行緒
		service.execute(new Runnable() {
			public void run() {
				try {

					String data1 = "執行緒2的資料";
					System.out.println("執行緒" + Thread.currentThread().getName() + "正在把資料:" + data1 + " 換出去");
					Thread.sleep((long) (Math.random() * 10000));

					// 執行緒2資料到了
					String data2 = (String) exchanger.exchange(data1);

					System.out.println("執行緒" + Thread.currentThread().getName() + "換回的資料為:" + data2);
				} catch (Exception e) {

				}
			}
		});

		try {
			Thread.sleep((long) (Math.random() * 100000));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	// -----------演示同步集合----------

	/**
	 * 同步集合 無可厚非,同步集合當然表示集合的讀寫安全 這樣不用自己去處理,很方便管理,但場合是否真正需要要自己把握
	 */
	public void thread_syncCollection() {

		// 同步Map
		Map<String, String> syncMap = new ConcurrentHashMap<String, String>();

		// 方式2
		Map<String, String> map = new HashMap<String, String>();
		Map<String, String> syncMap2 = new ConcurrentHashMap<String, String>(map);

		// 同步List
		List<String> syncList = new CopyOnWriteArrayList<String>();

		// 方式2
		List<String> list = new CopyOnWriteArrayList<String>();
		List<String> syncList2 = new CopyOnWriteArrayList<String>(list);

		// 同步Set
		Set<String> syncSet = new CopyOnWriteArraySet<String>();

		// 方式2
		Set<String> set = new CopyOnWriteArraySet<String>();
		Set<String> syncSet2 = new CopyOnWriteArraySet<String>(set);

	}

}