Java 多線程回顧

作java web開發,一直以來比較依賴java框架和oracle數據庫的功能。由於通常遇到高併發的狀況並很少,企業內軟件多半用戶數很少,即便偶爾遇到,也都在oracle數據庫中處理了。 java

對java的多線程開發,一直以來只有一個簡單的概念,沒有深刻使用理解過,但願此次利用網上的內容,可以回顧一下。 web

多線程相關概念

進程:一個內存中運行的應用程序,每一個進程都有本身獨立的一塊內存空間,一個進程中能夠啓動多個線程。好比在Windows系統中,一個運行的exe就是一個進程。 數據庫

線程:進程中的一個執行流程,一個進程中能夠運行多個線程。好比java.exe進程中能夠運行不少線程。線程老是屬於某個進程,進程中的多個線程共享進程的內存。 編程

操做系統中的進程是資源的組織單位。進程有一個包含了程序內容和數據的地址空間,以及其它的資源,包括打開的文件、子進程和信號處理器等。不一樣進程的地址空間是互相隔離的。而線程表示的是程序的執行流程,是CPU調度的基本單位。線程有本身的程序計數器、寄存器、棧和幀等。引入線程的動機在於操做系統中阻塞式I/O的存在。當一個線程所執行的I/O被阻塞的時候,同一進程中的其它線程可使用CPU來進行計算。這樣的話,就提升了應用的執行效率。線程的概念在主流的操做系統和編程語言中都獲得了支持。 多線程

線程與進程的區別: 併發

  • 地址空間: 進程內的一個執行單元;進程至少有一個線程,它們共享進程的地址空間;而進程有本身獨立的地址空間; oracle

  • 資源擁有: 進程是資源分配和擁有的單位,同一個進程內的線程共享進程的資源 框架

  • 線程是處理器調度的基本單位,但進程不是. 編程語言

  • 兩者都可併發執行. ide

線程相關的狀態以及轉換

線程共有下面4種狀態:

  • 新建狀態(New):新建立了一個線程對象,當你用new建立一個線程時,該線程還沒有運行。

  • 就緒狀態(Runnable):線程對象建立後,其餘線程調用了該對象的start()方法。該狀態的線程位於可運行線程池中,變得可運行,等待獲取CPU的使用權。

  • 運行狀態(Running):就緒狀態的線程獲取了CPU,執行程序代碼。

  • 阻塞狀態(Blocked):阻塞狀態是線程由於某種緣由放棄CPU使用權,暫時中止運行。直到線程進入就緒狀態,纔有機會轉到運行狀態。阻塞的狀況分三種:

    等待阻塞:運行的線程執行wait()方法,JVM會把該線程放入等待池中。

    同步阻塞:運行的線程在獲取對象的同步鎖時,若該同步鎖被別的線程佔用,則JVM把該線程放入鎖。

    其餘阻塞:運行的線程執行sleep()或join()方法,或者發出了I/O請求時,JVM會把該線程置爲阻塞狀態。當sleep()狀態超時、join()等待線程終止或者超時、或者I/O處理完畢時,線程從新轉入就緒狀態。

  • 死亡狀態(Dead)

    因爲run方法的正常退出而天然死亡;

    沒有捕獲到的異常事件終止了run方法的執行,從而致使線程忽然死亡

狀態之間的轉換能夠用下面的兩張圖來展現

thread color

thread detail

多線程實現方法

  • 繼承Thread類

    public class TestThread extends Thread { @Override public void run() { super.run(); for (int i = 0; i < 1000; i++) { System.out.println(「T-「 + i);

    try
              {
                  Thread.sleep(800);
              } catch (InterruptedException e)
              {
                  e.printStackTrace();
              }
    
          }
    
      }   }
  • 實現Runnable接口

    public class TestRunnable implements Runnable {

    @Override
      public void run()
      {
          for (int i = 0; i < 1000; i++)
          {
              System.out.println("R-" + i);
    
              try
              {
                  Thread.sleep(1000);
              } catch (InterruptedException e)
              {
                  e.printStackTrace();
              }
    
          }
      }
    
      public static void main(String[] args)
      {
          Thread runnable = new Thread(new TestRunnable());
          Thread thread = new TestThread();
          thread.start();
          runnable.start();
      }   }

運行結果:

thread console result

線程的分類

  • 守護線程(daemon threads)

守護線程一般爲普通線程提供服務,它們一般有一個無限循環,等待服務請求或執行線程的任務,在沒有其餘的非守護線程運行時,守護線程將中止工做。即便你不建立任何線程,Java應用程序在默認狀況下建立多個線程。他們大可能是守護線程,主要用於處理任務,如垃圾收集或JMX。

守護線程的特色:

  1. 優先級很是低

  2. 只有當同一個程序的任何其餘線程正在運行時執行。

  3. 當線程只剩下守護線程的時候,JVM就會退出.可是若是還有其餘的任意一個用戶線程還在,JVM就不會退出.

  • 非守護線程 (non-daemon threads)或普通線程或用戶線程(user threads)

用戶線程和守護線程二者幾乎沒有區別,惟一的不一樣之處就在於虛擬機的離開:若是用戶線程已經所有退出運行了,只剩下守護線程存在了,虛擬機也就退出了。 由於沒有了被守護者,守護線程也就沒有工做可作了,也就沒有繼續運行程序的必要了

在JAVA中經過調方法Thread.setDaemon(true)將線程轉爲守護線程。但須要注意的有:

  1. 但必須在調Thread.start()方法以前,由於一旦線程正在運行,你不能修改它的守護進程的狀態。

  2. 在Daemon線程中產生的新線程也是Daemon的

  3. 守護線程應該永遠不去訪問固有資源,如文件、數據庫,由於它會在任什麼時候候甚至在一個操做的中間發生中斷。

代碼示例:

public class DaemonThread
{

	public static void main(String[] args)
	{
		Thread thread = new Thread(new ThreadRunnable());
		thread.setDaemon(true);
		thread.start();

		try
		{
			Thread.sleep(750);
		} catch (InterruptedException e)
		{
			e.printStackTrace();
		}
		System.out.println("Main Thread ending");
	}

}

class ThreadRunnable implements Runnable
{

	@Override
	public void run()
	{
		int count = 0;
		while (true)
		{
			System.out.println("Hello from Worker " + count++);
			try
			{
				Thread.sleep(350);
			} catch (InterruptedException e)
			{
				e.printStackTrace();
			}
		}
	}
}

運行結果:

Hello from Worker 0
Hello from Worker 1
Hello from Worker 2
Main Thread ending

從運行結果能夠看到,隨着主線程的結果,原本是無限循環的子線程,也跟着結束了,說明守護線程不會影響程序的結束。

線程狀態的轉換

線程的狀態轉換,主要是由sleep()、wait()、yeid()、join()等幾個方法來實現(stop()、interrupt()等方法貌似都有必定的問題,不是很推薦使用)。

sleep方法與wait方法的區別:

sleep方法是靜態方法,wait方法是非靜態方法。

sleep方法在時間到後會本身「醒來」,但wait不能,必須由其它線程經過notify(All)方法讓它「醒來」。

sleep方法一般用在不須要等待資源狀況下的阻塞,像等待線程、數據庫鏈接的狀況通常用wait。

sleep/wait與yeld方法的區別:

調用sleep或wait方法後,線程即進入block狀態,而調用yeld方法後,線程進入runnable狀態。

wait與join方法的區別:

wait方法體現了線程之間的互斥關係,而join方法體現了線程之間的同步關係。

wait方法必須由其它線程來解鎖,而join方法不須要,只要被等待線程執行完畢,當前線程自動變爲就緒。

join方法的一個用途就是讓子線程在完成業務邏輯執行以前,主線程一直等待直到全部子線程執行完畢。
相關文章
相關標籤/搜索