1. 程式人生 > 程式設計 >深入理解java併發程式設計基礎篇(一)-------併發程式設計相關概念

深入理解java併發程式設計基礎篇(一)-------併發程式設計相關概念

一、前言

  拖了很久的併發程式設計,今天會開始第一篇,主要分為倆大部分進行學習:分為基礎篇以及進階篇,下面就開始基礎篇的學習。

二、併發程式設計的相關概念

2.1.同步(Sync)與非同步(Async)

2.2.1 同步(Sync)

  所謂同步,就是發出一個功能呼叫時,在沒有得到結果之前,該呼叫就不返回或繼續執行後續操作。
  根據這個定義,Java中所有方法都是同步呼叫,應為必須要等到結果後才會繼續執行。 我們在說同步、非同步的時候,一般而言是特指那些需要其他端協作或者需要一定時間完成的任務。簡單來說,同步就是必須一件一件事做,等前一件做完了才能做下一件事。

2.2.2 非同步(Async)

  非同步與同步相對,當一個非同步過程呼叫發出後,呼叫者在沒有得到結果之前,就可以繼續執行後續操作。 當這個呼叫完成後,一般通過狀態、通知和回撥來通知呼叫者。對於非同步呼叫,呼叫的返回並不受呼叫者控制。

如下圖示:

2.2.併發(Concurrency)和並行(Parallelism)

  併發和並行是兩個非常容易被混淆的概念。他們都可以表示兩個或者多個任務一起執行,但是側重點有所不同。併發偏重於多個任務交替執行,而多個任務之間有可能還是序列的,而並行是真正意義上的“同時執行”,如下圖:

  實際上,如果系統內只有一個CPU,而使用多程式或者多執行緒任務,那麼真實環境中這些任務不可能是真實並行的,畢竟一個CPU一次只能執行一條指令,在這種情況下多程式或者多執行緒就是併發的,而不是並行的(作業系統會不停地切換多工)。真實的並行也只可能出現在擁有多個CPU的系統中(比如多核CPU)。

2.3.臨界區

  臨界區表示公共資源或是共享資料,可以被多個執行緒使用。但是每次只能有一個執行緒使用它,一旦臨界區的資源被佔用,其他執行緒就必須等到資源釋放後才能繼續使用該資源。在Java程式開發中,對於這樣的資源一般都需要做同步的操作,例如下面的這段程式碼,用的就是synchronized關鍵字來對臨界區資源進行同步:

package com.MyMineBug.demoRun.test;

/**
 * 
 * @author 18360
 *
 */
public class SyncTest implements Runnable {

	// 臨界區資源
	public static SyncTest instance = new
SyncTest(); @Override public void run() { synchronized (instance) { } } public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(new SyncTest()); Thread t2 = new Thread(new SyncTest()); t1.start(); t2.start(); t1.join(); t2.join(); } } 複製程式碼

2.4. 阻塞(Blocking)和非阻塞(Non-Blocking)

  阻塞和非阻塞通常用來形容很多執行緒間的相互影響。比如一個執行緒佔用了共享資源,那麼其他所有需要這個資源的執行緒就必須在這個臨界區中等待。等待會導致執行緒掛起,這種情況就是阻塞。此時,如果佔用資源的執行緒一直不願意釋放資源,那麼其他執行緒阻塞在這個臨界區上的執行緒都不能工作。

  非阻塞的意思與之相反,它強調沒有一個執行緒可以妨礙其他執行緒執行,所有的執行緒都會嘗試不斷向前執行

2.5.死鎖(Deadlock)、飢餓(Starvation)和活鎖(Livelock)

2.5.1 死鎖(Deadlock)

  死鎖一般是指兩個或者兩個以上的執行緒互相持有對方所需的資源,並且永遠在等待對方釋放的一種阻塞狀態。例如有兩個執行緒A和B同時共享臨界區的資源C,當A佔用C時,B處於阻塞狀態,然而A的釋放需要用到B的資源,這樣一來,就變成了A一直在等待B,B也一直在等待A,互相之間永遠在等待對方釋放的狀態。

以下是死鎖的簡單例子:

package com.MyMineBug.demoRun.test;

public class Demo1 {

	public static String obj1 = "obj1";
	public static String obj2 = "obj2";

	public static void main(String[] args) {
		Thread a = new Thread(new Lock1());
		Thread b = new Thread(new Lock2());
		a.start();
		b.start();
	}

}

	class Lock1 implements Runnable {
	
		@Override
		public void run() {
			try {
				System.out.println("Lock1 running");
				while (true) {
					synchronized (Demo1.obj1) {
						System.out.println("Lock1 lock obj1");
						Thread.sleep(3000);// 獲取obj1後先等一會兒,讓Lock2有足夠的時間鎖住obj2
						synchronized (Demo1.obj2) {
							System.out.println("Lock1 lock obj2");
						}
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
	
		}
	
	}
	
	class Lock2 implements Runnable {
	
		@Override
		public void run() {
			try {
				System.out.println("Lock2 running");
				while (true) {
					synchronized (Demo1.obj2) {
						System.out.println("Lock2 lock obj2");
						Thread.sleep(3000);
						synchronized (Demo1.obj1) {
							System.out.println("Lock2 lock obj1");
						}
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
	
		}
	
	}


複製程式碼

執行結果如下:

一般來說,死鎖的發生是由於程式的設計不合理導致,而且死鎖很難解決,最好的方式就是預防.

2.5.2 飢餓(Starvation)

  飢餓是指某一個或者多個執行緒因為種種原因無法獲得所需的資源,導致一直無法執行。比如它的執行緒優先順序太低,而高優先順序的執行緒不斷搶佔它所需的資源,導致低優先順序資源無法工作。

2.5.3 活鎖(Livelock)

  活鎖的情況是執行緒一種非常有趣的情況,在生活中我們可能會碰到這樣的情況,那就是出門的時候可能會遇到有人要進門,你打算讓他先進門,他又打算讓你先出門,結果,兩個人都互相退後了,然後你打算先出門時對方也向前一步,來來回回就一直卡在門口。當然,這種事情正常人很快就能解決,但如果是執行緒碰到就沒那麼幸運了。

  如果兩個執行緒佔用著公共的資源,並且秉承著 “謙讓” 的原則,主動把資源讓給他人使用,你讓我也讓,這樣就造成資源在兩個執行緒間不斷跳動但執行緒之間都拿不到資源的情況,這樣的情況就是活鎖了。

  如果覺得還不錯,請點個贊!!!

  Share Technology And Love Life