/*************************************************************************************/
此博客主要是在觀看張孝祥老師的教學視頻的過程中,自己所做的學習筆記,在此以博客的形式分享出來,方便大家學習,建議大家觀看視頻,以此筆記作爲回顧資料。
參考資料
傳智播客_張孝祥_Java多線程與併發庫高級應用視頻教程下載 視頻下載
/*******************01張孝祥傳統線程技術回顧************************/
創建線程的兩種方式:
1,創建Thread的子類,重寫run方法
2,給Thread類傳入Runnable接口
兩種方式的區別:
第二種方式可以實現數據共享,而且更傾向於面向對象的編程思想。一般都是採用第二種方式。
new Thread().start();
調用了start方法後,就會運行Thread類的run方法,如下
public void run(){
if(target!=null){
targe.run();
}
}
如果target爲空,就什麼也不做
new Thread(
new Runnable(){
public void run() { //1
}
}
){
public void run() { //2
}
}.start();
執行的是2run方法
執行的步驟:
先運行子類的run方法,如果子類沒有重寫run方法,就去運行父類的run方法,上述代碼中子類重寫了run方法,所以就不會運行Runnable中的run方法。
/*******************02張孝祥傳統定時器技術回顧************************/
1秒後,炸一次
new Timer().schedule(new TimerTask() {
@Override
public void run() {
System.out.println("bombing!");
}
}, 1000);
每隔兩秒炸一次<一方式>
new Timer().schedule(new TimerTask() {
@Override
public void run() {
System.out.println("bombing!");
}
}, 1000,2000);
每隔兩秒鐘炸一次 <二方式>
new Timer().schedule(new MyTimerTask(), 2000);
class MyTimerTask extends TimerTask{
@Override
public void run() {
System.out.println("bombing!");
new Timer().schedule(new MyTimerTask(),2000);
}
}
注意:每個TimerTask()只能運行一次
先隔一秒炸一次,再隔兩秒鐘炸一次,再擱一秒鐘炸一次,。。。。
private static int count =0;
new Timer().schedule(new MyTimerTask(), 1000);
class MyTimerTask extends TimerTask{
@Override
public void run() {
count = (count+1)%2;
System.out.println("bombing!");
new Timer().schedule(new TimerTask(),1000+count*1000);
}
}
/*******************03張孝祥傳統線程互斥技術************************/
在靜態方法中,不能new內部類的實例對象
原因:
內部類,可以訪問外部類的成員變量,調用靜態方法的時候,沒有創建對象,此時沒有可以訪問的成員變量,所以會報錯。
回顧需要重新看視頻
/*******************04張孝祥傳統線程同步通信技術************************/
回顧需要重新看視頻
/*******************05張孝祥線程範圍內變量的概念************************/
線程內部共享數據,線程間數據獨立
package cn.itcast.heima2;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
public class ThreadScopeShareData {
private static Map<Thread, Integer> threadData = new HashMap<Thread, Integer>();
public static void main(String[] args) {
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() + "
has put data :" + data);
threadData.put(Thread.currentThread(), data);
new A().get();
new B().get();
}
}).start();
}
}
static class A{
public void get(){
int data = threadData.get(Thread.currentThread());
System.out.println("A from " + Thread.currentThread().getName() +
" get data :" + data);
}
}
static class B{
public void get(){
int data = threadData.get(Thread.currentThread());
System.out.println("B from " + Thread.currentThread().getName()+ "
get data :" + data);
}
}
}
/*******************06張孝祥ThreadLocal類及其應用技巧************************/
ThreadLocal類,實現了線程內部共享數據,線程間數據獨立,比05節視頻中的更加簡化方便
《1》
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
public class ThreadLocalTest {
public static void main(String[] args) {
new ThreadLocalTest().init();
}
//init
private void init(){
for(int i =0;i<2;i++){
new Thread(new Runnable() {
public void run() {
int data = new Random().nextInt();
Person.getThreadInstance().setName(Thread.currentThread().getName());
Person.getThreadInstance().setAge(data);
new A().get();
new B().get();
}
}).start();
}
}
//A
class A {
Person person = Person.getThreadInstance();
public void get(){
System.out.println("A:-"+Thread.currentThread().getName()+":name:"+person.getName()+":age:"+person.getAge());
}
}
//B
class B {
Person person = Person.getThreadInstance();
public void get(){
System.out.println("B:-"+Thread.currentThread().getName()+":name:"+person.getName()+":age:"+person.getAge());
}
}
//Person 將跟線程相關的綁定,放在共享的數據類的內部實現
static class Person{
private static ThreadLocal<Person> threadLocal = new ThreadLocal<ThreadLocalTest.Person>();
private Person(){
}
public static Person getThreadInstance(){
Person person = threadLocal.get();
if(person==null){
person = new Person();
threadLocal.set(person);
}
return person;.
}
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
}
《2》
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
public class ThreadLocalTest {
public static final ThreadLocal<Person> threadlocal = new ThreadLocal(){
@Override
protected Object initialValue() {
return new Person();
}
};
public static void main(String[] args) {
new ThreadLocalTest().init();
}
private void init(){
for(int i =0;i<2;i++){
new Thread(new Runnable() {
public void run() {
int data = new Random().nextInt();
threadlocal.get().setName(Thread.currentThread().getName());
threadlocal.get().setAge(data);
new A().get();
new B().get();
}
}).start();
}
}
//A
class A {
Person person = threadlocal.get();
public void get(){
System.out.println("A:-"+Thread.currentThread().getName()+":name:"+person.getName()+":age:"+person.getAge());
}
}
//B
class B {
Person person = threadlocal.get();
public void get(){
System.out.println("B:-"+Thread.currentThread().getName()+":name:"+person.getName()+":age:"+person.getAge());
}
}
//Person
static class Person{
public Person(){
}
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
}
/************07張孝祥多個線程之間共享數據的方式的討論**************/
如果每個線程執行的代碼相同,可以使用同一個Runnable對象,這個Runnable對象中有那個共享數據,例如,買票系統就可以這麼做。
如果每個線程執行的代碼不同,這時候需要用不同的Runnable對象,有如下兩種方式來實現這些Runnable對象之間的數據共享:
第一種: 將共享數據封裝在另外一個對象中,然後將這個對象逐一傳遞給各個Runnable對象。每個線程對共享數據的操作方法也分配到那個對象身上去完成,這樣容易實現針對該數據進行的各個操作的互斥和通信。
第二種:將這些Runnable對象作爲某一個類中的內部類,共享數據作爲這個外部類中的成員變量,每個線程對共享數據的操作方法也分配給外部類,以便實現對共享數據進行的各個操作的互斥和通信,作爲內部類的各個Runnable對象調用外部類的這些方法。
上面兩種方式的組合:將共享數據封裝在另外一個對象中,每個線程對共享數據的操作方法也分配到那個對象身上去完成,對象作爲這個外部類中的成員變量或方法中的局部變量,每個線程Runnable對象作爲外部類中的成員內部類或局部內部類。
總之,要同步互斥的幾段代碼最好是分別放在幾個獨立的方法中,這些方法再放在同一個類中,這樣比較容易實現它們之間的同步互斥和通信。
極端且簡單的方式,即在任意一個類中定義一個static的變量,這將被所有線程共享。
設計四個線程,其中兩個線程每次對j加一,另外兩個線程每次對j減一
第一種示例代碼
public class MultiThreadShareData {
private static ShareData shareData = new ShareData();
public static void main(String[] args) {
MyRunnable1 runNable1 = new MyRunnable1(shareData);
MyRunnable2 runNable2 = new MyRunnable2(shareData);
new Thread(runNable1).start();
new Thread(runNable2).start();
}
}
class ShareData{
private int j =0;
public ShareData(){
}
public void increment(){
j++;
}
public void decrement(){
j--;
}
}
class MyRunnable1 implements Runnable{
private ShareData shareData;
public MyRunnable1(ShareData shareData){
this.shareData = shareData;
}
public void run() {
this.shareData.increment();
}
}
class MyRunnable2 implements Runnable{
private ShareData shareData;
public MyRunnable2(ShareData shareData){
this.shareData = shareData;
}
public void run() {
this.shareData.decrement();
}
}
或者
public class MultiThreadShareData {
public static void main(String[] args) {
MultiThreadShareData multiThreadShareData = new MultiThreadShareData();
ShareData shareData = multiThreadShareData.new ShareData();
MyRunnable1 runNable1 = multiThreadShareData.new MyRunnable1(shareData);
MyRunnable2 runNable2 = multiThreadShareData.new MyRunnable2(shareData);
new Thread(runNable1).start();
new Thread(runNable2).start();
}
class ShareData{
private int j =0;
public ShareData(){
}
public void increment(){
j++;
}
public void decrement(){
j--;
}
}
class MyRunnable1 implements Runnable{
private ShareData shareData;
public MyRunnable1(ShareData shareData){
this.shareData = shareData;
}
public void run() {
this.shareData.increment();
}
}
class MyRunnable2 implements Runnable{
private ShareData shareData;
public MyRunnable2(ShareData shareData){
this.shareData = shareData;
}
public void run() {
this.shareData.decrement();
}
}
}
第二種示例代碼
public class MultiThreadShareData {
public static void main(String[] args) {
final ShareData shareData = new ShareData();
new Thread(new Runnable() {
public void run() {
shareData.increment();
}
}).start();
new Thread(new Runnable() {
public void run() {
shareData.decrement();
}
}).start();
}
}
class ShareData{
private int j =0;
public ShareData(){
}
public void increment(){
j++;
}
public void decrement(){
j--;
}
}
/********************08張孝祥Java5原子性操作類的應用************************/
/********************09張孝祥Java5線程並法庫的應用**************************/
線程池的概念與Executors類的應用
創建固定大小的線程池
創建緩存線程池
創建單一線程池(如何實現線程死亡後重新啓動)
關閉線程池
shutdown與shutdownNow的比較
用線程池啓動定時器
調用ScheduledExecutorService的schedule方法,返回的ScheduleFuture對象可以取消任務。支持間隔重複任務的定時方式,不直接支持絕對定時方式,需要轉換成相對時間方式。
創建一個固定線程數量的線程池,內有3個線程,分配給了10個任務,3個線程執行這10個任務,當一個線程執行完一個任務之後,再去執行另一個任務,直到所有的任務執行完畢,但線程池中線程不會銷燬。
ExecutorService executorService = Executors.newFixedThreadPool(3);
for(int i=1;i<=10;i++){
final int taskId = i;
executorService.execute(new Runnable() {
public void run() {
for(int j=1;j<=10;j++){
System.out.println(Thread.currentThread().getName()+"----"+j+"次"+"execute task"+taskId);
}
}
});
}
創建一個緩存線程池,緩存線程池中線程的數量是不固定的,動態變化,剛開始有3個任務,就只有3個線程,後來又來了6個任務,那就又增加了6個線程,任務執行完後,超時一段時間,多餘線程銷燬。
ExecutorService executorService = Executors.newCachedThreadPool();
for(int i=1;i<=10;i++){
final int taskId = i;
executorService.execute(new Runnable() {
public void run() {
for(int j=1;j<=10;j++){
System.out.println(Thread.currentThread().getName()+"----"+j+"次"+"execute task"+taskId);
}
}
});
}
executorService.shutdown(); //當所有線程都空閒的時候,殺死線程,終止程序。
executorService.shutdownNow();//不管線程中的任務有沒有執行完,都殺死線程。
創建一個只含有一個線程的線程池,該線程池只含有一個線程,當線程池裏的線程被銷燬後,線程池又會創建一個線程,替代原來的線程
ExecutorService executorService = Executors.newSingleThreadExecutor();
for(int i=1;i<=10;i++){
final int taskId = i;
executorService.execute(new Runnable() {
public void run() {
for(int j=1;j<=10;j++){
System.out.println(Thread.currentThread().getName()+"----"+j+"次"+"execute task"+taskId);
}
}
});
}
創建一個調度線程池,內含有3個線程,實現10秒定時執行功能
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);
scheduledExecutorService.schedule(new Runnable() {
public void run() {
System.out.println("bomb!!!");
}
},10, TimeUnit.SECONDS);
創建一個調度線程池,內含有3個線程,實現10秒定時執後,以後每隔2秒執行一次的功能。
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);
scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
public void run() {
System.out.println("bomb!!!");
}
},10, 2, TimeUnit.SECONDS);
/********************10張孝祥Callable與Future的應用**************************/
Future取得的結果類型和Callable返回的結果類型必須一致,這是通過泛型來實現的。
Callable要採用ExecutorSevice的submit方法提交,返回的future對象可以取消任務。
System.out.println("主線程::::"+Thread.currentThread().getName());
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future future = executorService.submit(new Callable() {
public Object call() throws Exception {
Thread.sleep(2000);
return Thread.currentThread().getName();
}
});
String string = null;
try {
System.out.println("等待開始");
string = (String) future.get();//沒有結果會一直等待,知道有結果爲止
//string = (String) future.get(10, TimeUnit.SECONDS);//等待10s,沒有有結果報異常
System.out.println("等待結束");
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Callable線程::::"+string);
CompletionService用於提交一組Callable任務,其take方法返回已完成的一個Callable任務對應的Future對象。
好比我同時種了幾塊地的麥子,然後就等待收割。收割時,則是那塊先成熟了,則先去收割哪塊麥子。
ExecutorService executorService = Executors.newFixedThreadPool(10);
CompletionService completionService = new ExecutorCompletionService(executorService);
for(int i=1;i<=10;i++){
final int taskId = i;
completionService.submit(new Callable() {
public Object call() throws Exception {
Thread.sleep(new Random().nextInt(5000));
return "執行完的任務的ID::::"+taskId;
}
});
}
for(int i=1;i<=10;i++){
try {
String string = (String) completionService.take().get();
System.out.println(string);
} catch (Exception e) {
e.printStackTrace();
}
}
/********************11張孝祥_java5的線程鎖技術**************************/
Lock比傳統線程模型中的synchronized方式更加面向對象,與生活中的鎖類似,鎖本身也應該是一個對象。兩個線程執行的代碼片段要實現同步互斥的效果,它們必須用同一個Lock對象。
public static void main(String[] args) {
new LockTest().action();
}
private void action(){
final Outputer outputer = new Outputer();
new Thread(new Runnable() {
public void run() {
for(int i=0;i<10;i++){
outputer.output("zhangxiaoxiang\n");
}
}
}).start();
new Thread(new Runnable() {
public void run() {
for(int i=0;i<10;i++){
outputer.output("lihuoming\n");
}
}
}).start();
}
private class Outputer{
private Lock lock = null;
public Outputer(){
lock = new ReentrantLock();
}
public void output(String name){
lock.lock();
try{
for(int i = 0;i<name.length();i++){
System.out.print(name.charAt(i));
};
}finally{
lock.unlock();
}
}
}
/********************12張孝祥java5讀寫鎖技術的妙用**************************/
讀寫鎖:分爲讀鎖和寫鎖,多個讀鎖不互斥,讀鎖與寫鎖互斥,這是由jvm自己控制的,你只要上好相應的鎖即可。如果你的代碼只讀數據,可以很多人同時讀,但不能同時寫,那就上讀鎖;如果你的代碼修改數據,只能有一個人在寫,且不能同時讀取,那就上寫鎖。總之,讀的時候上讀鎖,寫的時候上寫鎖!
package cn.itcast.heima2;
import java.util.Random;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockTest {
public static void main(String[] args) {
final Queue3 q3 = new Queue3();
for(int i=0;i<3;i++)
{
new Thread(){
public void run(){
while(true){
q3.get();
}
} }.start();
new Thread(){
public void run(){
while(true){
q3.put(new Random().nextInt(10000));
}
}
}.start();
}
}
}
class Queue3{
private Object data = null;//共享數據,只能有一個線程能寫該數據,但可以有多個線程同時讀該數據。
ReadWriteLock rwl = new ReentrantReadWriteLock();
public void get(){
rwl.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + " be ready to read data!");
Thread.sleep((long)(Math.random()*1000));
System.out.println(Thread.currentThread().getName() + "have read data :" + data);
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
rwl.readLock().unlock();
}
}
public void put(Object data){
rwl.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + " be ready to write data!");
Thread.sleep((long)(Math.random()*1000));
this.data = data;
System.out.println(Thread.currentThread().getName() + " have write data: " + data);
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
rwl.writeLock().unlock();
}
}
}
############################緩存系統示例代碼##############################
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class CacheDemo {
private Map cacheMap = new HashMap<String,Object>();
public static void main(String[] args) {
}
private ReadWriteLock rwl = new ReentrantReadWriteLock();
public Object get(int key) throws Exception{
rwl.readLock().lock();
Object value = null;
try{
value = cacheMap.get(key);
if(value==null){
rwl.readLock().unlock();
rwl.writeLock().lock();
try{
value = "aaaa";//實際上是queryDB()
if(value == null){
throw new Exception();
}
cacheMap.put(key, value);
}finally{
rwl.writeLock().unlock();
}
rwl.readLock().lock();
}
}finally{
rwl.readLock().unlock();
}
return value;
}
}
/**********13張孝祥_java5條件阻塞Condition的應用**************/
阻塞隊列
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class BoundedBuffer {
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();// notFull 緩存不滿
final Condition notEmpty = lock.newCondition();//notEmpty 緩存非空
final Object[] items = new Object[100];
int putptr,takeptr,count;
public void put(Object x) throws InterruptedException{
lock.lock();
try{
while(count==items.length)
notFull.await();//緩存不滿這個條件是假的 及意思是 緩存是滿的
items[putptr]=x;
if(++putptr==items.length) putptr=0;
++count;
notEmpty.signal();//緩存非空這個條件是真的
}finally{
lock.unlock();
}
}
public Object take() throws InterruptedException{
lock.lock();
try{
while(count==0)
notEmpty.await();//緩存非空這個條件是假的 及意思是 現在緩存是空的
Object x = items[takeptr];
if(++takeptr==items.length) takeptr=0;
--count;
notFull.signal();//緩存不滿這個條件是真的
return x;
}finally{
lock.unlock();
}
}
}
i
/********************14張孝祥java5的Semaphere同步工具**************************/
Semaphore可以維護當前訪問自身的線程個數,並提供了同步機制。使用Semaphore可以控制同時訪問資源的線程個數,例如,實現一個文件允許的併發訪問數。
Semaphore實現的功能就類似廁所有5個坑,假如有十個人要上廁所,那麼同時能有多少個人去上廁所呢?同時只能有5個人能夠佔用,當5個人中的任何一個人讓開後,其中在等待的另外5個人中又有一個可以佔用了。 另外等待的5個人中可以是隨機獲得優先機會,也可以是按照先來後到的順序獲得機會,這取決於構造Semaphore對象時傳入的參數選項。
單個信號量的Semaphore對象可以實現互斥鎖的功能,並且可以是由一個線程獲得了“鎖”,再由另一個線程釋放“鎖”,這可應用於死鎖恢復的一些場合。
package cn.itcast.heima2;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
public class TwoTest {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(3);
for(int i=0;i<10;i++){
Runnable runnable = new Runnable() {
public void run() {
try {
semaphore.acquire();
System.out.println("線程" + Thread.currentThread().getName() + "進入,當前已有"
+ (3-semaphore.availablePermits()) + "個併發");
Thread.sleep((long) (Math.random()*10000));
System.out.println("線程" + Thread.currentThread().getName() + "即將離開");
semaphore.release();
//下面代碼有時候執行不準確,因爲其沒有和上面的代碼合成原子單元
System.out.println("線程" + Thread.currentThread().getName() + "已離開,當前已有"
+ (3-semaphore.availablePermits()) + "個併發");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
executorService.execute(runnable);
}
}
}
/*********15張孝祥java5的CyclicBarrier同步工具************/
表示大家彼此等待,大家集合好後纔開始出發,分散活動後又在指定地點集合碰面,這就好比整個公司的人員利用週末時間集體郊遊一樣,先各自從家出發到公司集合後,再同時出發到公園遊玩,在指定地點集合後再同時開始就餐,…。
import java.util.Random;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CyclicBarrierTest {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
final CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
for(int i=1;i<=3;i++){
Runnable runnable = new Runnable() {
public void run() {
try {
Thread.sleep((long) (Math.random()*10000));
System.out.println("線程"+Thread.currentThread().getName()+"即將到達集合點1" +
",當前已有"+(cyclicBarrier.getNumberWaiting()+1)+"個到達集合點," +
(cyclicBarrier.getNumberWaiting()==2?"都到齊了,繼續走啊":"正在等候"));
cyclicBarrier.await();
Thread.sleep((long) (Math.random()*10000));
System.out.println("線程" + Thread.currentThread().getName() +
"即將到達集合地點2,當前已有" + (cyclicBarrier.getNumberWaiting()+1) + "個已經到達," + (cyclicBarrier.getNumberWaiting()==2?"都到齊了,繼續走啊":"正在等候"));
cyclicBarrier.await();
Thread.sleep((long)(Math.random()*10000));
System.out.println("線程" + Thread.currentThread().getName() +
"即將到達集合地點3,當前已有" + (cyclicBarrier.getNumberWaiting() + 1) + "個已經到達," + (cyclicBarrier.getNumberWaiting()==2?"都到齊了,繼續走啊":"正在等候"));
cyclicBarrier.await();
} catch (Exception e) {
e.printStackTrace();
}
}
};
executorService.execute(runnable);
}
executorService.shutdown();
}
}
/*********16張孝祥java5的CountDownLatch同步工具**********/
猶如倒計時計數器,調用CountDownLatch對象的countDown方法就將計數器減1,當計數到達0時,則所有等待者或單個等待者開始執行。這直接通過代碼來說明CountDownLatch的作用,這樣學員的理解效果更直接。
可以實現一個人(也可以是多個人)等待其他所有人都來通知他,這猶如一個計劃需要多個領導都簽字後才能繼續向下實施。還可以實現一個人通知多個人的效果,類似裁判一聲口令,運動員同時開始奔跑。用這個功能做百米賽跑的遊戲程序不錯哦!
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CountDownLatch {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
final java.util.concurrent.CountDownLatch orderCount = new java.util.concurrent.CountDownLatch(1);
final java.util.concurrent.CountDownLatch ansCount = new java.util.concurrent.CountDownLatch(3);
for(int i=1;i<=3;i++){
Runnable runnable = new Runnable() {
public void run() {
System.out.println("線程" + Thread.currentThread().getName() +"正準備接受命令");
try {
orderCount.await();
System.out.println("線程"
+ Thread.currentThread().getName() + "已接受命令");
Thread.sleep((long)(Math.random()*10000));
System.out.println("線程"
+ Thread.currentThread().getName() + "迴應命令處理結果");
ansCount.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
executorService.execute(runnable);
}
try {
Thread.sleep((long)(Math.random()*10000));
System.out.println("線程" + Thread.currentThread().getName()
+ "即將發佈命令");
orderCount.countDown();
System.out.println("線程" + Thread.currentThread().getName() +"已發送命令,正在等待結果");
ansCount.await();
System.out.println("線程" + Thread.currentThread().getName() +"已收到所有響應結果");
} catch (InterruptedException e) {
e.printStackTrace();
}
executorService.shutdown();
}
}
/*********17張孝祥java5的Exchanger同步工具*************/
用於實現兩個人之間的數據交換,每個人在完成一定的事務後想與對方交換數據,第一個先拿出數據的人將一直等待第二個人拿着數據到來時,才能彼此交換數據。
import java.util.concurrent.Exchanger;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ExchangerTest {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
final Exchanger exchanger = new Exchanger();
executorService.execute(new Runnable() {
public void run() {
try {
String data1 = "毒品";
System.out.println("線程" + Thread.currentThread().getName() +"正在把" + data1 +"換出去");
Thread.sleep((long) (Math.random()*10000));
String data2 = (String) exchanger.exchange(data1);
System.out.println("線程" + Thread.currentThread().getName() + "換回" + data2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
executorService.execute(new Runnable() {
public void run() {
try {
String data1 = "美金";
System.out.println("線程" + Thread.currentThread().getName() +"正在把" + data1 +"換出去");
Thread.sleep((long) (Math.random()*10000));
String data2 = (String) exchanger.exchange(data1);
System.out.println("線程" + Thread.currentThread().getName() + "換回" + data2);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
}
/***************
18張孝祥java5阻塞隊列的應用****************/
什麼是可阻塞隊列,阻塞隊列的作用與實際應用,阻塞隊列的實現原理。
阻塞隊列與Semaphore有些相似,但也不同,阻塞隊列是一方存放數據,另一方釋放數據,Semaphore通常則是由同一方設置和釋放信號量。
ArrayBlockingQueue 只有put方法和take方法才具有阻塞功能
用3個空間的隊列來演示阻塞隊列的功能和效果。
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.BlockingQueue;
public class BlockQueueTest {
/**
* @param args
*/
public static void main(String[] args) {
final BlockingQueue blockingQueue = new ArrayBlockingQueue(3);
for(int i=1;i<=2;i++){
new Thread(new Runnable() {
public void run() {
while(true){
try {
Thread.sleep((long) (Math.random()*10000));
System.out.println(Thread.currentThread().getName()+"準備放數據");
blockingQueue.put(1);
System.out.println(Thread.currentThread().getName()+"放數據成功"+"當前隊列有"+blockingQueue.size()+"個數據");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}).start();
}
new Thread(new Runnable() {
public void run() {
while(true){
try {
Thread.sleep((long) (Math.random()*10000));
System.out.println(Thread.currentThread().getName() + "準備取數據!");
blockingQueue.take();
System.out.println(Thread.currentThread().getName()+"取數據成功"+"當前隊列有"+blockingQueue.size()+"個數據");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
用兩個具有1個空間的隊列來實現同步通知的功能。
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class BlockingQueueCommunicationTest {
public static void main(String[] args) {
new BlockingQueueCommunicationTest().execute();
}
private void execute(){
final Business business = new Business();
new Thread(new Runnable() {
public void run() {
for(int j=1;j<=100;j++){
business.sub(j);
}
}
}).start();
for(int j=1;j<=100;j++){
business.main(j);
}
}
private class Business{
BlockingQueue blockingQueue1 = new ArrayBlockingQueue(1);
BlockingQueue blockingQueue2 = new ArrayBlockingQueue(1);
//匿名構造方法,先於非匿名構造方法執行
{
try {
blockingQueue2.put(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void sub(int j){
try {
blockingQueue1.put(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
for(int i=1;i<=10;i++){
System.out.println("sub thread sequece of " + i + ",loop of " + j);
}
try {
blockingQueue2.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void main(int j){
try {
blockingQueue2.put(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
for(int i=1;i<=10;i++){
System.out.println("main thread sequece of " + i + ",loop of " + j);
}
try {
blockingQueue1.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}