深刻理解java併發編程基礎篇(一)-------併發編程相關概念

1、前言

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

2、併發編程的相關概念

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

2.2.1 同步(Sync)

  所謂同步,就是發出一個功能調用時,在沒有獲得結果以前,該調用就不返回或繼續執行後續操做。
  根據這個定義,Java中全部方法都是同步調用,應爲必需要等到結果後纔會繼續執行。 咱們在說同步、異步的時候,通常而言是特指那些須要其餘端協做或者須要必定時間完成的任務。簡單來講,同步就是必須一件一件事作,等前一件作完了才能作下一件事。編程

2.2.2 異步(Async)

  異步與同步相對,當一個異步過程調用發出後,調用者在沒有獲得結果以前,就能夠繼續執行後續操做。 當這個調用完成後,通常經過狀態、通知和回調來通知調用者。對於異步調用,調用的返回並不受調用者控制。多線程

以下圖示:併發

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

  併發和並行是兩個很是容易被混淆的概念。他們均可以表示兩個或者多個任務一塊兒執行,可是側重點有所不一樣。併發偏重於多個任務交替執行,而多個任務之間有可能仍是串行的,而並行是真正意義上的「同時執行」,以下圖:異步

  實際上,若是系統內只有一個CPU,而使用多進程或者多線程任務,那麼真實環境中這些任務不多是真實並行的,畢竟一個CPU一次只能執行一條指令,在這種狀況下多進程或者多線程就是併發的,而不是並行的(操做系統會不停地切換多任務)。真實的並行也只可能出如今擁有多個CPU的系統中(好比多核CPU)。ide

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)

  阻塞和非阻塞一般用來形容不少線程間的相互影響。好比一個線程佔用了共享資源,那麼其餘全部須要這個資源的線程就必須在這個臨界區中等待。等待會致使線程掛起,這種狀況就是阻塞。此時,若是佔用資源的線程一直不肯意釋放資源,那麼其餘線程阻塞在這個臨界區上的線程都不能工做。spa

  非阻塞的意思與之相反,它強調沒有一個線程能夠妨礙其餘線程執行,全部的線程都會嘗試不斷向前執行操作系統

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

相關文章
相關標籤/搜索