線程與進程

簡而言之,一個程序至少有一個進程,一個進程至少有一個線程. 
線程的劃分尺度小於進程,使得多線程程序的併發性高。
另外,進程在執行過程當中擁有獨立的內存單元,而多個線程共享內存,從而極大地提升了程序的運行效率。
線程在執行過程當中與進程仍是有區別的。每一個獨立的線程有一個程序運行的入口、順序執行序列和程序的出口。可是線程不可以獨立執行,必須依存在應用程序中,由應用程序提供多個線程執行控制。
從邏輯角度來看,多線程的意義在於一個應用程序中,有多個執行部分能夠同時執行。但操做系統並無將多個線程看作多個獨立的應用,來實現進程的調度和管理以及資源分配。這就是進程和線程的重要區別。java

進程是具備必定獨立功能的程序關於某個數據集合上的一次運行活動,進程是系統進行資源分配和調度的一個獨立單位.
線程是進程的一個實體,是CPU調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位.線程本身基本上不擁有系統資源,只擁有一點在運行中必不可少的資源(如程序計數器,一組寄存器和棧),可是它可與同屬一個進程的其餘的線程共享進程所擁有的所有資源.
一個線程能夠建立和撤銷另外一個線程;同一個進程中的多個線程之間能夠併發執行.多線程

進程和線程的主要差異在於它們是不一樣的操做系統資源管理方式。進程有獨立的地址空間,一個進程崩潰後,在保護模式下不會對其它進程產生影響,而線程只是一個進程中的不一樣執行路徑。線程有本身的堆棧和局部變量,但線程之間沒有單獨的地址空間,一個線程死掉就等於整個進程死掉,因此多進程的程序要比多線程的程序健壯,但在進程切換時,耗費資源較大,效率要差一些。但對於一些要求同時進行而且又要共享某些變量的併發操做,只能用線程,不能用進程。併發

【java線程對象】this

在JAVA中,要開始一個線程,有兩種方式。一是直接調用Thread實例的start()方法,二是操作系統

  將Runable實例傳給一個Thread實例而後調用它的start()方法。線程

  在前面已經說過,線程對象和線程是兩個徹底不一樣的概念。這裏咱們再次深刻一下,生成一個線程的實例,並不表明啓動了線程。而啓動線程是說在某個線程對象上啓動了該實例對應的線程,當該線程結束後,並不會就當即消失。debug

  對於從不少書籍上能夠看到的基礎知識我就不用多說了。既然是基礎知識,我也着重於從普通文檔上讀不到的內容。因此本節我重點要說的是兩種線程對象產生線程方式的區別。code

class MyThread extends Thread{
 public int x = 0;
 public void run(){
  for(int i=0;i<100;i++){
    try{
     Thread.sleep(10);
    }catch(Exception e){}
    System.out.println(x++);
  }
 }
}

  若是咱們生成MyThread的一個實例,而後調用它的start()方法,那麼就產生了這個實例對應的線程:對象

public class Test {
 public static void main(String[] args) throws Exception{
  MyThread mt = new MyThread();
  mt.start();
 }
}

  不用說,最終會打印出0到99,如今咱們稍微玩一點花樣:接口

public class Test {
 public static void main(String[] args) throws Exception{
  MyThread mt = new MyThread();
  mt.start();
  System.out.println(101);
 }
}

  也不用說,在基礎篇(一)中咱們知道因爲單CPU的緣由,通常會先打印101,而後打印0到99。不過咱們能夠控制線程讓它按咱們的意思來運行:

public class Test {
 public static void main(String[] args) throws Exception{
  MyThread mt = new MyThread();
  mt.start();
  mt.join();
  System.out.println(101);
 }
}

  好了,咱們終於看到,mt實例對應的線程(假如我有時說mt線程請你不要怪我,不過我儘可能不這麼說)。在運行完成後,主線程纔打印101。由於咱們讓當前線程(這裏是主線程)等待mt線程的運行結束。"在線程對象a上調用join()方法,就是讓當前正在執行的線程等待線程對象a對應的線程運行完成後才繼續運行。" 請你們必定要深入理解並熟記這句話,而我這裏引出這個知識點的目的是爲了讓你繼續看下面的例子:

public class Test {
 public static void main(String[] args) throws Exception{
  MyThread mt = new MyThread();
  mt.start();
  mt.join();
  Thread.sleep(3000);
  mt.start();
 }
}

  當線程對象mt運行完成後,咱們讓主線程休息一下,而後咱們再次在這個線程對象上啓動線程。結果咱們看到:

  Exception in thread "main" java.lang.IllegalThreadStateException

  也就是這種線程對象一時運行一次完成後,它就不再能運行第二次了。咱們能夠看一下它有具體實現:

public synchronized void start() {
 if (started)
  throw new IllegalThreadStateException();
  started = true;
  group.add(this);
  start0();
 }

  一個Thread的實例一旦調用start()方法,這個實例的started標記就標記爲true,事實中無論這個線程後來有沒有執行到底,只要調用了一次start()就再也沒有機會運行了,這意味着:

  [經過Thread實例的start(),一個Thread的實例只能產生一個線程]

  那麼若是要在一個實例上產生多個線程(也就是咱們常說的線程池),咱們應該如何作呢?這就是Runnable接口給咱們帶來的偉大的功能。

class R implements Runnable{
 private int x = 0;
 public void run(){
  for(int i=0;i<100;i++){
    try{
     Thread.sleep(10);
    }catch(Exception e){}
    System.out.println(x++);
  }
 }
}

  正如它的名字同樣,Runnable的實例是可運行的,但它本身並不能直接運行,它須要被Thread對象來包裝才行運行:

public class Test {
 public static void main(String[] args) throws Exception{
  new Thread(new R()).start();
 }
}

  固然這個結果和mt.start()沒有什麼區別。但若是咱們把一個Runnable實例給Thread對象屢次包裝,咱們就能夠看到它們實際是在同一實例上啓動線程:

public class Test {
 public static void main(String[] args) throws Exception{
  R r = new R();
  for(int i=0;i<10;i++)
    new Thread(r).start();
 }
}

  x是實例對象,但結果是x被加到了999(理想狀態下),說明這10個線程是在同一個r對象上運行的。請你們注意,由於這個例子是在單CPU上運行的,因此沒有對多個線程同時操做共同的對象進行同步。這裏是爲了說明的方便而簡化了同步,而真正的環境中你沒法預知程序會在什麼環境下運行,因此必定要考慮同步。

  到這裏咱們作一個完整的例子來講明線程產生的方式不一樣而生成的線程的區別:

package debug;
import java.io.*;
import java.lang.Thread;
class MyThread extends Thread{
 public int x = 0;
 public void run(){
  System.out.println(++x);
 }
}

class R implements Runnable{
 private int x = 0;
 public void run(){
  System.out.println(++x);
 }
}
public class Test {
 public static void main(String[] args) throws Exception{
  for(int i=0;i<10;i++){
    Thread t = new MyThread();
    t.start();
  }
  Thread.sleep(10000);//讓上面的線程運行完成
  R r = new R();
  for(int i=0;i<10;i++){
    Thread t = new Thread(r);
    t.start();
  }
 }  
}

  上面10個線程對象產生的10個線程運行時打印了10次1。下面10個線程對象產生的10個線程運行時打印了1到10。咱們把下面的10個線程稱爲同一實例(Runnable實例)的多個線程。

總結:

Thread方式:一個線程對象只能產生一個線程,屢次啓動操做的不是同一個線程對象

Runnable方式:一個線程對象能夠產生多個線程,多個線程同時啓動操做的是同一個線程對象

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息