Java併發學習(一)
阿新 • • 發佈:2019-01-31
1.要響應執行緒中斷
執行緒接受到中斷訊號後要及時的對中斷進行響應。響應方式:
1.捕捉InterruptException
for(;;){
try {
doXXX();
} catch (InterruptedException e) {
System.out.println(getName() +" is interrupt");
break;
}
catch (Exception e) {
e.printStackTrace();
}
}
2.判斷當前執行緒狀態
for(;;){ doXXX(); //判斷是否被中斷 if(Thread.interrupted()){ System.out.println(getName() +" is interrupt"); break; } }
接收到中斷訊號後,需要結束當前執行緒,可行的方式有return,break等,比較優雅的方式是丟擲InterruptedException異常。程式碼如下:
public void foo() throws InterruptedException {
if (Thread.interrupted()) {
throw new InterruptedException();
}
}
2.使用ThreadLocal
顧名思義它是local variable(執行緒區域性變數)。它的功用非常簡單,就是為每一個使用該變數的執行緒都提供一個變數值的副本,是每一個執行緒都可以獨立地改變自己的副本,而不會和其它執行緒的副本衝突。從執行緒的角度看,就好像每一個執行緒都完全擁有該變數。- 不使用ThreadLocal的測試結果
輸出結果:出現併發問題private int sum2 = 0; public int getSum(){ sum2++; return sum2; } public static void main(String[] args){ new CurrentTest().threadLocal(); } public void threadLocal(){ TestThreadLocal t1 = new TestThreadLocal(this); TestThreadLocal t2 = new TestThreadLocal(this); TestThreadLocal t3 = new TestThreadLocal(this); t1.setName("t1"); t2.setName("t2"); t3.setName("t3"); t1.start(); t2.start(); t3.start(); } class TestThreadLocal extends Thread{ CurrentTest c; public TestThreadLocal(CurrentTest c) { this.c = c; } @Override public void run() { int sum = this.c.getSum(); while(sum<10){ System.out.println(getName()+" sum is "+sum); sum = this.c.getSum(); } } }
- 使用ThreadLocal的測試結果
private ThreadLocal<Integer> sum = new ThreadLocal<Integer>(){
protected Integer initialValue() {return 0;};
};
public int getSum(){
sum.set(sum.get() + 1);
return sum.get();
}
public static void main(String[] args){
new CurrentTest().threadLocal();
}
public void threadLocal(){
TestThreadLocal t1 = new TestThreadLocal(this);
TestThreadLocal t2 = new TestThreadLocal(this);
TestThreadLocal t3 = new TestThreadLocal(this);
t1.setName("t1");
t2.setName("t2");
t3.setName("t3");
t1.start();
t2.start();
t3.start();
}
class TestThreadLocal extends Thread{
CurrentTest c;
public TestThreadLocal(CurrentTest c) {
this.c = c;
}
@Override
public void run() {
int sum = this.c.getSum();
while(sum<10){
System.out.println(getName()+" sum is "+sum);
sum = this.c.getSum();
}
}
}
輸出結果:每個執行緒都從1一直計數到9,執行緒間沒有出現併發問題
事實上ThreadLocal是犧牲空間來減少高併發所消耗的時間,其原理是每個Thread維護一個Map集合,集合的Key是ThreadLocal物件,value是共享變數的副本,這樣每次Thread修改變數時就會直接修改本地儲存的變數副本。具體ThreadLocal原始碼如下:
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
注意:使用ThreadLocal,一般都是宣告在靜態變數中,如果不斷的建立ThreadLocal而且沒有呼叫其remove方法,將會導致記憶體洩露。
3.任務的提交者和執行者
為了方便併發執行任務,出現了一種專門用來執行任務的實現,也就是Executor。由此,任務提交者不需要再建立管理執行緒,使用更方便,也減少了開銷。java.util.concurrent.Executors是Executor的工廠類,通過Executors可以建立你所需要的Executor。
常用的子類如下圖:(轉自:http://blog.csdn.net/qq_29882587/article/details/78658675) 更廣泛的使用是ExecutorSevice介面,主要API如下: 其中submit方法可以接收兩類引數,Runable和Callable,Callable是需要有返回值的。submit方法返回Feture物件。Feture物件用於執行緒間的通訊,Future通常包括get(阻塞至任務完成), cancel,get(timeout)(等待一段時間)等等。Future也用於非同步變同步的場景。