一、若是每一個線程執行的代碼相同,可使用同一個Runnable對象,這個Runnable對象中有那個共享數據,例如,買票系統就能夠這麼作。html
二、若是每一個線程執行的代碼不一樣,這時候須要用不一樣的Runnable對象,有以下兩種方式來實現這些Runnable對象之間的數據共享:java
上面兩種方式的組合:將共享數據封裝在另一個對象中,每一個線程對共享數據的操做方法也分配到那個對象身上去完成,對象做爲這個外部類中的成員變量或方法中的局部變量,每一個線程的Runnable對象做爲外部類中的成員內部類或局部內部類。程序員
總之,要同步互斥的幾段代碼最好是分別放在幾個獨立的方法中,這些方法再放在同一個類中,這樣比較容易實現它們之間的同步互斥和通訊。算法
極端且簡單的方式,即在任意一個類中定義一個static的變量,這將被全部線程共享。數據庫
示例代碼緩存
1 package com.chunjiangchao.thread; 2 /** 3 * 多線程之間數據共享 4 * @author chunjiangchao 5 * 6 */ 7 public class MultiThreadShareDataDemo { 8 9 public static void main(String[] args) { 10 Data data = new Data(); 11 new Thread(new IncrementRunnable(data)).start(); 12 new Thread(new DecrementtRunnable(data)).start(); 13 14 final Data data2 = new Data(); 15 new Thread(new Runnable() { 16 17 @Override 18 public void run() { 19 data2.increment(); 20 } 21 }).start(); 22 new Thread(new Runnable() { 23 24 @Override 25 public void run() { 26 data2.decrement(); 27 } 28 }).start(); 29 } 30 //對共享數據進行增長 31 private static class IncrementRunnable implements Runnable{ 32 private Data data ; 33 public IncrementRunnable(Data data){ 34 this.data = data; 35 } 36 public void run() { 37 data.increment(); 38 } 39 } 40 //對共享數據進行減小 41 private static class DecrementtRunnable implements Runnable{ 42 private Data data ; 43 public DecrementtRunnable(Data data){ 44 this.data = data; 45 } 46 public void run() { 47 data.decrement(); 48 } 49 } 50 51 52 //共享數據 53 private static class Data{ 54 private int temp=0; 55 public synchronized void increment(){ 56 temp++; 57 System.out.println(Thread.currentThread()+"中temp的值爲:"+temp); 58 } 59 public synchronized void decrement(){ 60 temp--; 61 System.out.println(Thread.currentThread()+"中temp的值爲:"+temp); 62 } 63 } 64 65 }
static ExecutorService newFixedThreadPool(int nThreads) 建立一個可重用固定線程數的線程池,以共享的無界隊列方式來運行這些線程。(建立固定線程池)多線程
若是在這個線程池裏面,建立的線程爲3個線程,可是交給的任務時10個任務的話,那麼,線程池裏面的線程就會運行完3個線程後,接着運行3個線程,直到全部的線程運行完畢。併發
List<Runnable> shutdownNow()試圖中止全部正在執行的活動任務,暫停處理正在等待的任務,並返回等待執行的任務列表。dom
shutdown()啓動一次順序關閉,執行之前提交的任務,但不接受新任務。jvm
static ExecutorService newCachedThreadPool():
建立一個可根據須要建立新線程的線程池,可是在之前構造的線程可用時將重用它們。(動態建立線程池,有多少任務,自動建立多少線程)
static ExecutorService newSingleThreadExecutor():建立單個線程,若是線程死掉了,它會自動找個替補線程補上去。(如何實現線程死掉以後從新啓動)?
static ScheduledExecutorService newScheduledThreadPool(int corePoolSize):建立一個定時線程池
實例代碼:
1 package com.chunjiangchao.thread; 2 3 import java.util.Date; 4 import java.util.concurrent.ExecutorService; 5 import java.util.concurrent.Executors; 6 import java.util.concurrent.TimeUnit; 7 8 /** 9 * 線程併發庫,線程池的使用 10 * @author chunjiangchao 11 * 12 */ 13 public class ExecuterDemo { 14 15 public static void main(String[] args) { 16 // ExecutorService threadPool = Executors.newFixedThreadPool(3);//開了固定的三個線程 17 // ExecutorService threadPool = Executors.newCachedThreadPool();//開了10個線程 18 ExecutorService threadPool = Executors.newSingleThreadExecutor();//開了一個固定的線程 19 for(int i=0;i<10;i++){ 20 final int loop = i; 21 threadPool.execute(new Runnable(){ 22 public void run() { 23 try { 24 Thread.sleep(1000); 25 } catch (InterruptedException e) { 26 // e.printStackTrace(); 27 } 28 System.out.println(Thread.currentThread().getName()+" outer "+loop); 29 } 30 31 }); 32 } 33 /* 34 shutdownNow執行的結果爲: 35 pool-1-thread-3 outer 36 pool-1-thread-1 outer 37 pool-1-thread-2 outer * */ 38 // threadPool.shutdownNow(); 39 /*shutdown會執行完全部已經提交的任務,不會處理shutdown後提交的任務,並且在後面提交Runnable的時候, 40 * 會拋出異常java.util.concurrent.RejectedExecutionException*/ 41 threadPool.shutdown(); 42 // threadPool.execute(new Runnable(){ 43 // 44 // @Override 45 // public void run() { 46 // System.out.println("不會進行處理"); 47 // } 48 // 49 // }); 50 //實現定時器效果 51 Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(new Runnable(){ 52 53 @Override 54 public void run() { 55 System.out.println("執行定時器結果"+new Date().toLocaleString()); 56 } 57 58 }, 2, 4, TimeUnit.SECONDS);//每隔4s玩一次 59 } 60 61 }
Future取得的結果類型和Callable返回的結果類型必須一致,這是經過泛型來實現的。
Callable要採用ExecutorSevice的submit方法提交,返回的future對象能夠取消任務。
CompletionService用於提交一組Callable任務,其take方法返回已完成的一個Callable任務對應的Future對象。
take() 獲取並移除表示下一個已完成任務的 Future,若是目前不存在這樣的任務,則等待
示例代碼
1 package com.chunjiangchao.thread; 2 3 import java.util.Date; 4 import java.util.concurrent.Callable; 5 import java.util.concurrent.ExecutionException; 6 import java.util.concurrent.Executor; 7 import java.util.concurrent.ExecutorCompletionService; 8 import java.util.concurrent.ExecutorService; 9 import java.util.concurrent.Executors; 10 import java.util.concurrent.Future; 11 12 /** 13 * Callable&Future的使用 14 * @author chunjiangchao 15 * 16 */ 17 public class CallableAndFutureDemo { 18 19 public static void main(String[] args) { 20 ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor(); 21 //提交單一任務 22 Future<String> submit = newSingleThreadExecutor.submit(new Callable<String>(){ 23 24 @Override 25 public String call() throws Exception { 26 printTime(); 27 mSleep(3000); 28 printTime(); 29 return "我這有返回值,你看看是否是"; 30 } 31 32 }); 33 mSleep(500); 34 try { 35 String string = submit.get(); 36 System.out.println(string); 37 } catch (InterruptedException | ExecutionException e) { 38 e.printStackTrace(); 39 } 40 // submit.cancel(true);//能夠對任務進行取消 41 //提交多個任務 42 Executor executor = Executors.newCachedThreadPool(); 43 ExecutorCompletionService<String> completionService = new ExecutorCompletionService<>(executor); 44 for(int i=0;i<10;i++){ 45 final int loop = i; 46 completionService.submit(new Callable<String>(){ 47 48 @Override 49 public String call() throws Exception { 50 mSleep(1000*loop); 51 return "提交多任務有返回結果"+loop; 52 } 53 54 }); 55 } 56 for(int i=0;i<10;i++){ 57 try { 58 Future<String> result = completionService.take(); 59 printTime(); 60 System.out.println(result.get()); 61 } catch (InterruptedException e) { 62 e.printStackTrace(); 63 } catch (ExecutionException e) { 64 e.printStackTrace(); 65 } 66 } 67 /* 68 * 打印 結果以下 69 2016-4-18 11:57:46 70 2016-4-18 11:57:49 71 我這有返回值,你看看是否是 72 2016-4-18 11:57:49 73 提交多任務有返回結果0 74 2016-4-18 11:57:50 75 提交多任務有返回結果1 76 2016-4-18 11:57:51 77 提交多任務有返回結果2 78 2016-4-18 11:57:52 79 提交多任務有返回結果3 80 2016-4-18 11:57:53 81 提交多任務有返回結果4 82 2016-4-18 11:57:54 83 提交多任務有返回結果5 84 2016-4-18 11:57:55 85 提交多任務有返回結果6 86 2016-4-18 11:57:56 87 提交多任務有返回結果7 88 2016-4-18 11:57:57 89 提交多任務有返回結果8 90 2016-4-18 11:57:58 91 提交多任務有返回結果9 92 */ 93 94 } 95 private static void mSleep(long time){ 96 try { 97 Thread.sleep(time); 98 } catch (InterruptedException e) { 99 e.printStackTrace(); 100 } 101 } 102 private static void printTime(){ 103 System.out.println(new Date().toLocaleString()); 104 } 105 106 }
Lock的使用
1 package com.chunjiangchao.thread; 2 3 import java.util.concurrent.locks.Lock; 4 import java.util.concurrent.locks.ReentrantLock; 5 6 /** 7 * lock的使用 8 */ 9 public class LockDemo { 10 11 public static void main(String[] args) { 12 final Outputer outputer = new Outputer(); 13 for(int index=0;index<10;index++){ 14 final int loop = index; 15 new Thread(new Runnable() { 16 public void run() { 17 // outputer.print("chunjiangchao"+loop); 18 outputer.synPrint("chunjiangchao"+loop); 19 } 20 }).start(); 21 22 } 23 } 24 private static class Outputer{ 25 private Lock lock = new ReentrantLock(); 26 public void print(String name){ 27 int length = name.length(); 28 lock.lock(); 29 try { 30 for(int i=0;i<length;i++){ 31 Thread.sleep(100); 32 System.out.print(name.charAt(i)+" "); 33 } 34 System.out.println(); 35 } catch (Exception e) { 36 e.printStackTrace(); 37 }finally{ 38 lock.unlock(); 39 } 40 41 } 42 /** 43 * 同步代碼塊的做用,和上面添加Lock鎖的做用相同,只不過鎖的對象不同而已 44 * @param name 45 */ 46 public synchronized void synPrint(String name){ 47 int length = name.length(); 48 try { 49 for(int i=0;i<length;i++){ 50 Thread.sleep(100); 51 System.out.print(name.charAt(i)+" "); 52 } 53 System.out.println(); 54 } catch (Exception e) { 55 e.printStackTrace(); 56 } 57 } 58 } 59 60 }
Lock比傳統線程模型中的synchronized方式更加面向對象,與生活中的鎖相似,鎖自己也應該是一個對象。兩個線程執行的代碼片斷要實現同步互斥的效果,它們必須用同一個Lock對象。Lock lock= new ReentrantLock( )
ReadWriteLock rwl = new ReentrantReadWriteLock( )
讀寫鎖:分爲讀鎖和寫鎖,多個讀鎖不互斥,讀鎖與寫鎖互斥,這是由jvm本身控制的,你只要上好相應的鎖便可。若是你的代碼只讀數據,能夠不少人同時讀,但不能同時寫,那就上讀鎖;若是你的代碼修改數據,只能有一我的在寫,且不能同時讀取,那就上寫鎖。總之,讀的時候上讀鎖,寫的時候上寫鎖!
示例代碼(讀鎖與讀鎖併發,寫鎖與寫鎖併發,讀鎖與寫鎖互斥)
1 package com.chunjiangchao.thread; 2 3 import java.util.Random; 4 import java.util.concurrent.locks.ReadWriteLock; 5 import java.util.concurrent.locks.ReentrantReadWriteLock; 6 /** 7 * 使用讀寫鎖 查看打印結果發現讀鎖與讀鎖之間併發,寫鎖與寫鎖間併發,讀與寫之間是互斥的 8 * @author chunjaingchao 9 * 10 */ 11 public class ReadWriteLockDemo { 12 public static void main(String[] args) { 13 final Queue q3 = new Queue(); 14 for(int i=0;i<3;i++) 15 { 16 new Thread(){ 17 public void run(){ 18 while(true){ 19 q3.get(); 20 } 21 } 22 }.start(); 23 new Thread(){ 24 public void run(){ 25 while(true){ 26 q3.put(new Random().nextInt(10000)); 27 } 28 } 29 }.start(); 30 } 31 } 32 static class Queue{ 33 private Integer integer = null;//共享數據,只能有一個線程能寫該數據,但能夠有多個線程同時讀該數據。 34 ReadWriteLock rwl = new ReentrantReadWriteLock(); 35 public void get(){ 36 rwl.readLock().lock(); 37 try { 38 System.out.println(Thread.currentThread().getName() + "*****讀取******"); 39 Thread.sleep(200); 40 System.out.println(Thread.currentThread().getName() + "******讀取*****" + integer); 41 } catch (InterruptedException e) { 42 e.printStackTrace(); 43 }finally{ 44 rwl.readLock().unlock(); 45 } 46 } 47 public void put(Integer data){ 48 rwl.writeLock().lock(); 49 try { 50 System.out.println(Thread.currentThread().getName() + "######寫數據#######"); 51 Thread.sleep(200); 52 this.integer = data; 53 System.out.println(Thread.currentThread().getName() + "#######寫數據#######" + data); 54 } catch (InterruptedException e) { 55 e.printStackTrace(); 56 }finally{ 57 rwl.writeLock().unlock(); 58 } 59 } 60 } 61 }
在線程操做某個方法,執行這個方法的時候。
本身掛寫鎖,而後本身掛讀鎖也是能夠的(由於這是在當前線程同一個方法中的)。本身掛寫鎖,是爲了防止其餘人進入程序進行寫的操做。可是,不該該進制本身進入。(在Hibernate中,鎖分爲讀鎖、寫鎖、更新鎖)
在JDKAPI中有相關的實例代碼以下
1 class CachedData { 2 Object data; 3 volatile boolean cacheValid; 4 ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); 5 void processCachedData() { 6 rwl.readLock().lock();//添加讀鎖 7 if (!cacheValid) { 8 // Must release read lock before acquiring write lock 9 rwl.readLock().unlock();//若是沒有數據,將讀鎖釋放 10 rwl.writeLock().lock();//添加寫鎖 11 // Recheck state because another thread might have acquired 12 // write lock and changed state before we did. 13 if (!cacheValid) { 14 data = ... 15 cacheValid = true; 16 } 17 // Downgrade by acquiring read lock before releasing write lock 18 rwl.readLock().lock();//添加讀鎖 19 rwl.writeLock().unlock(); // Unlock write, still hold read//釋放寫鎖 20 } 21 use(data); 22 rwl.readLock().unlock();//釋放讀鎖 23 } 24 }
問題:設計緩存系統
緩存系統的概念:你要找數據不要直接去找數據庫,能夠直接找我。 我若是沒有,查找數據庫給你。與你直接查找是同樣的。好處就是下一次你再來的時候,我就不用操做數據庫了。我直接給你。
在等待 Condition 時,容許發生「虛假喚醒」,這一般做爲對基礎平臺語義的讓步。對於大多數應用程序,這帶來的實際影響很小,由於 Condition 應該老是在一個循環中被等待,並測試正被等待的狀態聲明。某個實現能夠隨意移除可能的虛假喚醒,但建議應用程序程序員老是假定這些虛假喚醒可能發生,所以老是在一個循環中等待。(記住:每次在等待的時候,都要將判斷放在while循環中,防止僞喚醒出現)
一個鎖內部能夠有多個Condition,即有多路等待和通知,能夠參看jdk1.5提供的Lock與Condition實現的可阻塞隊列的應用案例,從中除了要體味算法,還要體味面向對象的封裝。在傳統的線程機制中一個監視器對象上只能有一路等待和通知,要想實現多路等待和通知,必須嵌套使用多個同步監視器對象。(若是隻用一個Condition,兩個放的都在等,一旦一個放的進去了,那麼它通知可能會致使另外一個放接着往下走。)
問題:此處爲何要建立兩個Condition對象?只建立一個不就好了?
答:若是本道程序只有兩個線程的話,只建立一個Condition對象就好了。若是是超過4個線程。例如兩個存放線程、兩個讀取線程。若是你只建立一個Condition對象,在signal的時候,會喚醒全部都處在等待狀態的線程。而不是針對某一種類型的線程。(沒有針對性)
在API文檔中有以下實例代碼
1 class BoundedBuffer { 2 final Lock lock = new ReentrantLock(); 3 final Condition notFull = lock.newCondition(); 4 final Condition notEmpty = lock.newCondition(); 5 final Object[] items = new Object[100]; 6 int putptr, takeptr, count; 7 public void put(Object x) throws InterruptedException { 8 lock.lock(); 9 try { 10 while (count == items.length) 11 notFull.await(); 12 items[putptr] = x; 13 if (++putptr == items.length) putptr = 0; 14 ++count; 15 notEmpty.signal(); 16 } finally { 17 lock.unlock(); 18 } 19 } 20 public Object take() throws InterruptedException { 21 lock.lock(); 22 try { 23 while (count == 0) 24 notEmpty.await(); 25 Object x = items[takeptr]; 26 if (++takeptr == items.length) takeptr = 0; 27 --count; 28 notFull.signal(); 29 return x; 30 } finally { 31 lock.unlock(); 32 } 33 } 34 }
利用BoundedBuffer寫一個簡單是生產者消費者模式
1 public class BoundedBufferDemo { 2 3 public static void main(String[] args) { 4 final BoundedBuffer boundedBuffer = new BoundedBuffer(); 5 new Thread(new Runnable(){ 6 7 @Override 8 public void run() { 9 while(true){ 10 try { 11 Thread.sleep(1000); 12 int nextInt = new Random().nextInt(); 13 System.out.println(new Date().toLocaleString()+"存放數據"+nextInt); 14 boundedBuffer.put(nextInt); 15 } catch (InterruptedException e) { 16 e.printStackTrace(); 17 } 18 } 19 } 20 21 }).start(); 22 new Thread(new Runnable(){ 23 24 @Override 25 public void run() { 26 while(true){ 27 try { 28 Thread.sleep(new Random().nextInt(1000)); 29 System.out.println(new Date().toLocaleString()+"獲取數據"+boundedBuffer.take()); 30 } catch (InterruptedException e) { 31 e.printStackTrace(); 32 } 33 } 34 } 35 36 }).start(); 37 } 38 }
問題:子線程循環10次,接着主線程循環100,接着又回到子線程循環10次,接着再回到主線程又循環100,如此循環50次,請寫出程序。
1 package com.chunjiangchao.thread; 2 3 import java.util.concurrent.locks.Condition; 4 import java.util.concurrent.locks.Lock; 5 import java.util.concurrent.locks.ReentrantLock; 6 7 /** 8 * 相似於生產者消費者 9 * 子線程循環10次,接着主線程循環100,接着又回到子線程循環10次,接着再回到主線程又循環100,如此循環50次,請寫出程序。 10 * @author chunjiangchao 11 */ 12 public class ConditionDemo { 13 14 public static void main(String[] args) { 15 final Business business = new Business(); 16 new Thread(new Runnable() { 17 public void run() { 18 for(int i = 0;i<10;i++){ 19 business.sub(i); 20 } 21 } 22 }).start(); 23 new Thread(new Runnable() { 24 public void run() { 25 for(int i = 0;i<10;i++){ 26 business.main(i); 27 } 28 } 29 }).start(); 30 } 31 private static class Business{ 32 private Lock lock = new ReentrantLock(); 33 private Condition condition = lock.newCondition(); 34 private boolean bShouldSub = true; 35 public void main(int loop){ 36 lock.lock(); 37 while(bShouldSub){ 38 try { 39 condition.await(); 40 } catch (InterruptedException e) { 41 e.printStackTrace(); 42 } 43 } 44 for(int i=0;i<100;i++){ 45 try { 46 Thread.sleep(10); 47 } catch (InterruptedException e) { 48 e.printStackTrace(); 49 } 50 System.out.println(loop+"……main……"+i); 51 } 52 bShouldSub = true; 53 condition.signal(); 54 lock.unlock();//應該寫在finally代碼塊裏面 55 } 56 public void sub(int loop){ 57 lock.lock(); 58 while(!bShouldSub){ 59 try { 60 condition.await(); 61 } catch (InterruptedException e) { 62 e.printStackTrace(); 63 } 64 } 65 for(int i=0;i<10;i++){ 66 try { 67 Thread.sleep(10); 68 } catch (InterruptedException e) { 69 e.printStackTrace(); 70 } 71 System.out.println(loop+"……sub……"+i); 72 } 73 bShouldSub = false; 74 condition.signal(); 75 lock.unlock();//應該寫在finally代碼塊裏 76 } 77 78 } 79 }
問題:怎樣實現3個線程的交互通訊?
1 package com.chunjiangchao.thread; 2 3 import java.util.concurrent.locks.Condition; 4 import java.util.concurrent.locks.Lock; 5 import java.util.concurrent.locks.ReentrantLock; 6 7 /** 8 * 多線程之間的通訊 9 * 三個線程交互執行 A-B-C-A-B-C 10 * @author chunjiangchao 11 * 12 */ 13 public class ThreeConditionDemo { 14 15 public static void main(String[] args) { 16 final Business business = new Business(); 17 new Thread(new Runnable() { 18 public void run() { 19 for(int i = 0;i<10;i++){ 20 business.one(i); 21 } 22 } 23 }).start(); 24 new Thread(new Runnable() { 25 public void run() { 26 for(int i = 0;i<10;i++){ 27 business.two(i); 28 } 29 } 30 }).start(); 31 new Thread(new Runnable() { 32 public void run() { 33 for(int i = 0;i<10;i++){ 34 business.three(i); 35 } 36 } 37 }).start(); 38 } 39 private static class Business{ 40 private Lock lock = new ReentrantLock(); 41 private Condition condition1 = lock.newCondition(); 42 private Condition condition2 = lock.newCondition(); 43 private Condition condition3 = lock.newCondition(); 44 private int whichOne = 1; 45 public void one(int loop){ 46 try { 47 lock.lock(); 48 while(whichOne!=1){ 49 condition1.await(); 50 } 51 for(int i=0;i<10;i++){ 52 Thread.sleep(10); 53 System.out.println("one "+loop+" 當前執行 "+i); 54 } 55 whichOne = 2; 56 condition2.signal(); 57 } catch (Exception e) { 58 e.printStackTrace(); 59 }finally{ 60 lock.unlock(); 61 } 62 } 63 public void two(int loop){ 64 try { 65 lock.lock(); 66 while(whichOne!=2){ 67 condition2.await(); 68 } 69 for(int i=0;i<10;i++){ 70 Thread.sleep(10); 71 System.out.println("two "+loop+" 當前執行 "+i); 72 } 73 whichOne = 3; 74 condition3.signal(); 75 } catch (Exception e) { 76 e.printStackTrace(); 77 }finally{ 78 lock.unlock(); 79 } 80 } 81 public void three(int loop){ 82 try { 83 lock.lock(); 84 while(whichOne!=3){ 85 condition3.await(); 86 } 87 for(int i=0;i<10;i++){ 88 Thread.sleep(10); 89 System.out.println("three "+loop+" 當前執行 "+i); 90 } 91 whichOne = 1; 92 condition1.signal(); 93 } catch (Exception e) { 94 e.printStackTrace(); 95 }finally{ 96 lock.unlock(); 97 } 98 } 99 } 100 101 }
未完待續……