1. 程式人生 > >java併發程式設計的藝術(一):併發程式設計的挑戰

java併發程式設計的藝術(一):併發程式設計的挑戰

併發程式設計的挑戰主要是在三個方面

  1. 上下文切換
  2. 死鎖
  3. 資源限制

下面就這三個方面進行分別分析遇到的挑戰以及如何應對。

1)上下文切換

     1.1 什麼是上下文切換?多執行緒一定快麼?

      想了解上下文切換,我們先來了解幾個概念。我們都知道在一塊CPU上進行多執行緒,實際上是CPU通過時間分配演算法給每個執行緒分配了不同的時間片,時間片就是CPU分配給每個執行緒的執行時間,由於時間片的單位量級是幾十毫秒,因此我們感覺到多個執行緒在同時執行。但實際上,從當前任務切換到下一個任務之前,會儲存上一個任務的狀態,以便下次切換到這個任務時候,可以直接載入它上次的狀態。

      因此上下文切換指的就是任務從儲存到再載入的過程就是一次上下文切換。但是由於這個切換需要進行狀態的儲存,因此是需要代價的,會影響效率。

    因為存在上下文切換,因此多執行緒執行不一定不單執行緒快。

package chanllenge;

public class ConcurrencyTest {
	private static final long count = 10000L;
	public static void main(String[] args) {
		concurrency();
		serial();
	}
	private static void serial() {
		// TODO Auto-generated method stub
				long start = System.currentTimeMillis();
				
						int a = 0;
						for(long i=0;i<count;i++ ){
							a+=5;
						}
				
				int b = 0;
				for(long j=0;j<count;j++){
					b--;
				}
				
				
				long time = System.currentTimeMillis()-start;
				
				System.out.println("serial :"+time+"ms,b="+b+",a="+a);
				
				
				
				
	}
	private static void concurrency() {
		// TODO Auto-generated method stub
		// TODO Auto-generated method stub
				long start = System.currentTimeMillis();
				Thread t = new Thread(new Runnable() {
					@Override
					public void run() {
						// TODO Auto-generated method stub
						int a = 0;
						for(long i=0;i<count;i++ ){
							a+=5;
						}
					}
				});
				t.start();
				
				int b = 0;
				for(long j=0;j<count;j++){
					b--;
				}
				
				try {
					t.join();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				long time = System.currentTimeMillis()-start;
				
				System.out.println("concurrency :"+time+"ms,b="+b);
	}
	
	
}

如上程式碼,最終執行結果為:

concurrency :2ms,b=-10000
serial :0ms,b=-10000,a=50000

顯然,此時多執行緒執行的反而慢。

1.2 如何應對上下文切換的挑戰?

     瞭解了什麼是上下文切換,我們只要從原理上入手,就很容易知道怎麼應對上下文挑戰——儘可能的減少執行緒或讓執行緒分工明確,減少進行切換。如下是減少上下文切換的一些方法。

      1.2.1 無鎖併發程式設計

                多執行緒競爭鎖的時候,會引起上下文切換。所以我們可以採用分段處理資料等方式來避免使用鎖。

       1.2.2 CAS演算法

                Java的Atomic包使用CAS演算法來更新資料,不需要加鎖。

       1.2.3 使用最小執行緒

                避免建立不需要的執行緒,比如任務很少,就不需要建立很多執行緒。

       1.2.4 協程

              在單執行緒裡實現多工排程,並在單執行緒中維持多個任務間的切換。

  2)死鎖

    死鎖,是大家多執行緒中一個基礎的概念,在此不再贅述,僅簡述避免死鎖的一些方法。

    2.1 避免一個執行緒同時獲取兩個鎖

   2. 2 避免一個執行緒在鎖內佔用多個資源

   2.3 嘗試使用定時鎖

  2.4 對於資料庫鎖,加鎖解鎖必須在一個數據庫連結中

3) 資源限制

     資源限制又分為硬體,軟體。硬體資源限制有伺服器頻寬,硬碟讀寫速度,CPU處理速度。軟體資源限制有資料庫連結數、socket連結數等。

   原則上我們使程式加快速度方式為使序列變並行,但是如果受限於資源,序列反而會比並行執行的更快。

    應對資源限制,就是靠money啊啊啊啊啊