背景:併發知識是一個程序員段位升級的體現,一樣也是進入BAT的必經之路,有必要把併發知識從新梳理一遍。java
併發concurrent:程序員
使用ThreadLocal能夠實現線程範圍內共享變量,線程A寫入的值和線程B獲取到的結果一致;ReentrantReadWriteLock容許多個讀線程或多個寫線程同時進行,但不容許寫線程和讀線程同時進行;使用Callable能夠獲得線程執行的返回結果;Exchanger能夠相互交換家線程執行的結果;這些使用方法大體都同樣,JDk參考文檔裏面哪裏不會點哪裏,下面寫個ThreadLocal實現線程範圍內變量共享,裏面還用到了一下餓漢模式:面試
執行結果如圖中控制檯打印,使用ThreadLocal保證了線程0和線程1讀取到的值與寫入的一致。算法
1 import java.util.Random; 2 import java.util.concurrent.locks.ReentrantReadWriteLock; 3 import lombok.Data; 4
5 public class ThreadLocalTest { 6 //ThreadLocal 實現線程範圍內共享變量
7 private static ThreadLocal<Integer> x = new ThreadLocal<Integer>(); 8 private static ThreadLocal<MyThreadScopeData> myThreadScopeData = new ThreadLocal<MyThreadScopeData>(); 9 public static void main(String[] args) { 10 new ReentrantReadWriteLock(); 11 for(int i = 0; i<2; i++) { 12 new Thread(new Runnable() { 13 @Override 14 public void run() { 15 int data = new Random().nextInt(); 16 System.out.println(Thread.currentThread().getName() 17 +" has put data: "+ data); 18 x.set(data); // 存的時候與當前線程相關 取的時候也是與當前線程相關 19 //MyThreadScopeData.getInstance()拿到與本線程實例相關的對象: 不用反覆new對象來getter/setter
20 MyThreadScopeData.getThreadInstance().setName("name: "+data); 21 MyThreadScopeData.getThreadInstance().setAge(data); 22 new A().get(); 23 new B().get(); 24 } 25 }).start(); 26 } 27 } 28
29 static class A{ 30 public void get() { 31 int data = x.get(); 32 System.out.println("A from "+Thread.currentThread().getName()+" get data: "+data); 33 //MyThreadScopeData.getInstance()拿到與本線程實例相關的對象
34 MyThreadScopeData myData = MyThreadScopeData.getThreadInstance(); 35 System.out.println("A from "+Thread.currentThread().getName() 36 +" getMyData: "+myData.getName() +","+myData.getAge()); 37 } 38 } 39
40 static class B{ 41 public void get() { 42 int data = x.get(); 43 System.out.println("B from "+Thread.currentThread().getName()+" get data: "+data); 44 //MyThreadScopeData.getInstance()拿到與本線程實例相關的對象
45 MyThreadScopeData myData = MyThreadScopeData.getThreadInstance(); 46 System.out.println("B from "+Thread.currentThread().getName() 47 +" getMyData: "+myData.getName() +","+myData.getAge()); 48 } 49 } 50
51 //設計本身線程範圍內變量的共享,不須要建立對象,只需調用線程便可用到線程內的變量
52 @Data 53 static class MyThreadScopeData{ 54 //構造方法私有化,外部沒發直接調用,可是能夠調用裏面的靜態方法
55 private MyThreadScopeData() { } 56 public static /*synchronized*/ MyThreadScopeData getThreadInstance() { 57 MyThreadScopeData instance = map.get(); 58 if (instance == null) { 59 //餓漢模式 : 第一次來建立
60 instance = new MyThreadScopeData(); 61 map.set(instance); 62 } 63 return instance; 64 } 65 // private static MyThreadScopeData instance = null; // new MyThreadScopeData();
66 private static ThreadLocal<MyThreadScopeData> map = new ThreadLocal<MyThreadScopeData>(); 67 private String name; 68 private int age; 69 } 70
71 }
網上還有一個多線程面試頗有趣的題目:子線程執行10次,主線程執行100次,接着子線程再執行10次,主線程繼續再執行100次,往復循環50次;spring
1 //Java多線程面試: 子線程執行10次,主線程執行100次,接着子線程再10次,主線程再執行100次,往復循環50次
2 public class ThreadCommunication { 3 public static void main(String[] args) { 4 Business business = new Business(); 5 new Thread( 6 new Runnable() { 7 @Override 8 public void run() { 9 for (int i = 1; i <= 50; i++) { 10 business.sub(i); 11 } 12 } 13 }).start(); 14
15 for (int i = 1; i <= 50; i++) { 16 business.main(i); 17 } 18 } 19
20 //把主線程和自線程執行的方法歸結到一個類(共同算法的若干方法),巧妙設計,好維護高聚合,健壯性;
21 public static class Business{ 22 //子線程方法
23 private boolean bShouldSub = true; 24 public synchronized void sub(int i) { 25 while(!bShouldSub) { 26 //用while比if更好,能夠防止線程被僞喚醒
27 try { 28 this.wait(); // 若是不是子線程方法該執行的,則令其等待
29 } catch (InterruptedException e) { 30 e.printStackTrace(); 31 } 32 } 33 for(int j = 1; j<= 10; j++) { 34 System.out.println("sub thread sequence of "+j + ",loop of "+ i); 35 } 36 bShouldSub = false; 37 this.notify(); //喚醒主線程方法
38 } 39
40 //主線程方法
41 public synchronized void main(int i) { 42 while(!bShouldSub) { 43 try { 44 this.wait(); // 若是不是主線程方法該執行的,則令其等待
45 } catch (InterruptedException e) { 46 e.printStackTrace(); 47 } 48 } 49 for(int j = 1; j<= 100; j++) { 50 System.out.println("main thread sequence of "+j + ",loop of "+ i); 51 } 52 bShouldSub = true; 53 this.notify(); // 喚醒子線程方法
54 } 55 } 56 }
線程池:緩存
一、固定線程數目的線程池newFixedThreadPool;多線程
二、緩存線程數目的線程池newCachedThreadPool;併發
三、單一線程池newSingleThreadExecutor;dom
四、定時器線程池newScheduledThreadPool;ide
1 package com.xinyan.springcloud.controller; 2
3 import java.util.concurrent.ExecutorService; 4 import java.util.concurrent.Executors; 5 import java.util.concurrent.TimeUnit; 6
7 public class ThreadPoolTest { 8 public static void main(String[] args) { 9 //固定線程數目的線程池 3個
10 ExecutorService threadPool = Executors.newFixedThreadPool(3); 11 //緩存線程數目的線程池即動態變化 當任務過來了,線程池內部會自動增長線程,空閒後線程又被回收,線程數目不定 12 //ExecutorService threadPool = Executors.newCachedThreadPool(); 13 //單一線程池 14 //ExecutorService threadPool = Executors.newSingleThreadExecutor(); 15 //往線程池中放入10個任務:
16 for(int i = 1; i<= 10; i++) { 17 final int task = i; // task被final修飾不能變了,可是i 能夠變
18 threadPool.execute(new Runnable() { 19 @Override 20 public void run() { 21 for(int j =1; j<=10; j++) { 22 try { 23 Thread.sleep(200); 24 } catch (InterruptedException e) { 25 e.printStackTrace(); 26 } 27 //System.out.println(Thread.currentThread().getName()+" loop of "+ j +" task is "+ task);
28 } 29 } 30 }); 31 } 32 System.out.println("全部的10個任務已經所有提交。"); //任務都提交了,交由線程池去搞
33 threadPool.shutdown(); //沒有任務後關閉線程 34 //threadPool.shutdownNow(); //還有任務沒有給執行完畢就當即關閉線程 35
36 //定時器線程池: 3個線程
37 System.out.println("敵軍還有5秒到達戰場."); 38 Executors.newScheduledThreadPool(3).schedule(new Runnable() { 39 @Override 40 public void run() { 41 System.out.println("敵軍抵達戰場,碾碎她們。"); 42 } 43 //5秒後執行線程池內run方法
44 }, 5, TimeUnit.SECONDS); 45 //Executors.newScheduledThreadPool(3)scheduleAtFixedRate(command, initialDelay, period, unit) 46 //scheduleAtFixedRate 定時循環執行線程池內方法
47 } 48 }