天天看點

Android多線程、多程序間的通信

   1、 作業系統的任務排程

    在window和Linux作業系統中,任務排程采用時間片輪轉機制,因為CPU執行效率非常高,記憶體執行效率比較低,為了提高CPU使用率,采用時間片輪轉,就是一個任務執行一段時間之後強制暫停去執行下一個任務,每個任務輪流執行,被暫停的任務處于就緒狀态,等待下一個屬于它的時間片到來,這樣每個任務都得到執行,而并發的意思就是多個任務同時執行。

    并行:是指多個處理器或者多核處理器同僚處理多個不同的任務。

    并發:一個處理器同僚處理多個任務。

       并發和并行與多線程的關系是:并行需要兩個或者兩個以上的線程運作在不同的處理器上,而并發可以再一個處理器上通過時間片輪轉進行切換。

    2、程序線程定義

    作業系統負責任務排程,資源配置設定和管理,統領整個計算機硬體,應用程式則是具有某種功能的程式,運作于作業系統之上。

    程序:程序是具有獨立功能的程式在一個資料集上的一次動态執行過程,是作業系統進行資源配置設定和排程的一個獨立機關,是應用程式運作的載體。程序一般由程式、資料集合、和程序控制塊三部分組成。

    程序是程式的一次執行,是臨時的有生命周期的,動态産生動态消亡,任何程序可以同其他程序一起并發執行,程序是系統進行資源配置設定和排程的獨立機關,程序由程式,資料和控制塊組成。

    線程:線程的作用簡單來說是可以讓程序中的不同代碼塊同時執行,線程是程式執行的單一順序的控制流程,程式執行的最小單元,是處理器排程和分派的基本機關。各個線程之間共享程序的記憶體空間。一個線程由線程ID、目前指令指針PC、寄存器和堆棧組成。

    3、android中的多程序通信方法

    每個程序都對應虛拟機配置設定的一塊獨立的記憶體空間,不同的程序之間記憶體獨立,(每個應用程式就是一個程序,當然一個應用程式可以有多個程序)如果兩個程序之間需要共享資料,就需要用到IPC就是跨程序通信。

    Android的SDK提供四種跨程序通信方式,對應四大元件。Activity可以跨程序調用其他應用程式的Activity,content Provider可以跨程序通路其他應用程式的的資料,另外broadcast可以向系統所有應用發送廣播,service之間可以通過AIDL實作程序間通信。

    (1)Activity:Activity跨程序不需要制定Context對象和Activity的class對象,需要通路的是Activity所對赢得action,和URI,是Intent的第二個參數。

    如:Intent callIntent = new Intent(Intent.ACTION_CALL,Uri.parse(“tel:123456”));

           startActivity(callIntent);

    (2)ContentProvider

     Android應用程式需可以使用檔案或者Sqlite共享存儲資料,可以利用contentProvider進行增删查改,如音頻、視訊、聯系人資訊、通過這些ContentProvider可以擷取相應的資訊清單,這些清單以Cursor對象傳回。

    (3)廣播

    發送廣播需要調用sendBroadcast方法,該方法需要一個Intent對象,通過Intent可以發送,廣播需要的資料,利用廣播通信會有明顯的延時,并且因為廣播是向整個作業系統發送,容易出現被其他應用程式攔截的情況。

    (4)service

    service既不是線程也不是程序,如果需要執行耗時操作,需要新開一個線程實作,這個時候可以使用IntentService實作,因為IntentService總的HandAction實際上是一個線程可以處理耗時操作。

    使用AIDL實際上是CS架構,這個時候如果需要回報資料,需要進行兩邊同時定義AIDL接口,然後進行資料的回傳。

4、AIDL通信

    實作AIDL通信對于需要傳遞一個自定義對象的資料,需要序列化,序列化分為兩種方法Serializable和Parcelable。

    4.1序列化的目的:

    (1)永久儲存對象資料,将對象儲存在檔案或磁盤中

    (2)通過序列化将對象資料,進行網絡傳輸,把對象資料轉換為位元組流方式

    (3)将對象資料在程序間進行傳遞,在目前Activity中進行序列化操作寫入,在另一個中進行反序列化操作讀出。

    (4)将資料儲存至資料庫和檔案中

    (5)序列化隻針對對象不針對方法

    (6)Intent之間傳遞資料,複雜的可以使用序列化操作。

    4.2 android中實作序列化兩種方法。

    (1)Serialization接口

寫一個實體類 Person,利用Java自帶的Serializable進行序列化

package com.amqr.serializabletest.entity;
import java.io.Serializable;
/**
 * User: LJM
 * Date&Time: 2016-02-22 & 14:16
 * Describe: Describe Text
 */
public class Person implements Serializable{
    private static final long serialVersionUID = ;
    private String name;
    private int age;
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
                

使用,MainActivity和SecondActivity結合使用

MainActivity

package com.amqr.serializabletest;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import com.amqr.serializabletest.entity.Person;
/**
 * 進行Android開發的時候,我們都知道不能将對象的引用傳給Activities或者Fragments,
 * 我們需要将這些對象放到一個Intent或者Bundle裡面,然後再傳遞。
 *
 *
 * Android中Intent如果要傳遞類對象,可以通過兩種方式實作。
 * 方式一:Serializable,要傳遞的類實作Serializable接口傳遞對象,
 * 方式二:Parcelable,要傳遞的類實作Parcelable接口傳遞對象。
 *
 * Serializable(Java自帶):
 * Serializable是序列化的意思,表示将一個對象轉換成可存儲或可傳輸的狀态。序列化後的對象可以在網絡上進行傳輸,也可以存儲到本地。
 *
 * Parcelable(android 專用):
 * 除了Serializable之外,使用Parcelable也可以實作相同的效果,
 * 不過不同于将對象進行序列化,Parcelable方式的實作原理是将一個完整的對象進行分解,
 * 而分解後的每一部分都是Intent所支援的資料類型,這樣也就實作傳遞對象的功能了。
 要求被傳遞的對象必須實作上述2種接口中的一種才能通過Intent直接傳遞。
 */
public class MainActivity extends Activity {
    private TextView mTvOpenNew;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.mTvOpenNew).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent open = new Intent(MainActivity.this,SecondActivity.class);
                Person person = new Person();
                person.setName("一去二三裡");
                person.setAge();
                // 傳輸方式一,intent直接調用putExtra
                // public Intent putExtra(String name, Serializable value)
                open.putExtra("put_ser_test", person);
                // 傳輸方式二,intent利用putExtras(注意s)傳入bundle
                /**
                Bundle bundle = new Bundle();
                bundle.putSerializable("bundle_ser",person);
                open.putExtras(bundle);
                 */
                startActivity(open);
            }
        });
    }
}
                

SecondActivity

package com.amqr.serializabletest;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.widget.TextView;
import com.amqr.serializabletest.entity.Person;
/**
 * User: LJM
 * Date&Time: 2016-02-22 & 11:56
 * Describe: Describe Text
 */
public class SecondActivity extends Activity{
    private TextView mTvDate;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        mTvDate = (TextView) findViewById(R.id.mTvDate);
        Intent intent = getIntent();
        // 關鍵方法:getSerializableExtra ,我們的類是實作了Serializable接口的,是以寫這個方法獲得對象
        // public class Person implements Serializable
        Person per = (Person)intent.getSerializableExtra("put_ser_test");
        //Person per = (Person)intent.getSerializableExtra("bundle_ser");
        mTvDate.setText("名字:"+per.getName()+"\\n"
                +"年齡:"+per.getAge());
    }
}                

連結:https://www.jianshu.com/p/a60b609ec7e7

    (2)Parcelable接口

我們寫一個實體類,實作Parcelable接口,馬上就被要求

1、複寫describeContents方法和writeToParcel方法

2、執行個體化靜态内部對象CREATOR,實作接口Parcelable.Creator 。

也就是,随便一個類實作了Parcelable接口就一開始就會變成這樣子

Parcelable方式的實作原理是将一個完整的對象進行分解,而分解後的每一部分都是Intent所支援的資料類型,這樣也就實作傳遞對象的功能了。

public class Pen implements Parcelable{
    private String color;
    private int size;
    protected Pen(Parcel in) {
        color = in.readString();
        size = in.readInt();
    }
    public static final Creator<Pen> CREATOR = new Creator<Pen>() {
        @Override
        public Pen createFromParcel(Parcel in) {
            return new Pen(in);
        }
        @Override
        public Pen[] newArray(int size) {
            return new Pen[size];
        }
    };
    @Override
    public int describeContents() {
        return ;
    }
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(color);
        dest.writeInt(size);
    }
}
                

系統已經幫我們做了很多事情,我們需要做的很簡單,就寫寫我們自己需要的構造方法,寫一下私有變量的get和set

大概變成這樣子:

package com.amqr.serializabletest.entity;
import android.os.Parcel;
import android.os.Parcelable;
/**
 * User: LJM
 * Date&Time: 2016-02-22 & 14:52
 * Describe: Describe Text
 */
public class Pen implements Parcelable{
    private String color;
    private int size;
    
    // 系統自動添加,給createFromParcel裡面用
    protected Pen(Parcel in) {
        color = in.readString();
        size = in.readInt();
    }
    public static final Creator<Pen> CREATOR = new Creator<Pen>() {
        /**
         *
         * @param in
         * @return
         * createFromParcel()方法中我們要去讀取剛才寫出的name和age字段,
         * 并建立一個Person對象進行傳回,其中color和size都是調用Parcel的readXxx()方法讀取到的,
         * 注意這裡讀取的順序一定要和剛才寫出的順序完全相同。
         * 讀取的工作我們利用一個構造函數幫我們完成了
         */
        @Override
        public Pen createFromParcel(Parcel in) {
            return new Pen(in); // 在構造函數裡面完成了 讀取 的工作
        }
        //供反序列化本類數組時調用的
        @Override
        public Pen[] newArray(int size) {
            return new Pen[size];
        }
    };
    
    @Override
    public int describeContents() {
        return ;  // 内容接口描述,預設傳回0即可。
    }
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(color);  // 寫出 color
        dest.writeInt(size);  // 寫出 size
    }
    // ======分割線,寫寫get和set
    //個人自己添加
    public Pen() {
    }
    //個人自己添加
    public Pen(String color, int size) {
        this.color = color;
        this.size = size;
    }
    
    public String getColor() {
        return color;
    }
    public void setColor(String color) {
        this.color = color;
    }
    public int getSize() {
        return size;
    }
    public void setSize(int size) {
        this.size = size;
    }
}

                

其實說起來Parcelable寫起來也不是很麻煩,在as裡面,我們的一個實體類寫好私有變量之後,讓這個類繼承自Parcelable,接下的步驟是:

1、複寫兩個方法,分别是describeContents和writeToParcel

2、執行個體化靜态内部對象CREATOR,實作接口Parcelable.Creator 。 以上這兩步系統都已經幫我們自動做好了

3、自己寫寫我們所需要的構造方法,變量的get和set

實作自Parcelable實體Bean已經寫好了,接下來我們結合MainActivity和ThirdActivity來使用以下:

MainActivity

package com.amqr.serializabletest;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import com.amqr.serializabletest.entity.Pen;
import com.amqr.serializabletest.entity.Person;
/**
 * 進行Android開發的時候,我們都知道不能将對象的引用傳給Activities或者Fragments,
 * 我們需要将這些對象放到一個Intent或者Bundle裡面,然後再傳遞。
 *
 *
 * Android中Intent如果要傳遞類對象,可以通過兩種方式實作。
 * 方式一:Serializable,要傳遞的類實作Serializable接口傳遞對象,
 * 方式二:Parcelable,要傳遞的類實作Parcelable接口傳遞對象。
 *
 * Serializable(Java自帶):
 * Serializable是序列化的意思,表示将一個對象轉換成可存儲或可傳輸的狀态。序列化後的對象可以在網絡上進行傳輸,也可以存儲到本地。
 *
 * Parcelable(android 專用):
 * 除了Serializable之外,使用Parcelable也可以實作相同的效果,
 * 不過不同于将對象進行序列化,Parcelable方式的實作原理是将一個完整的對象進行分解,
 * 而分解後的每一部分都是Intent所支援的資料類型,這樣也就實作傳遞對象的功能了。
 要求被傳遞的對象必須實作上述2種接口中的一種才能通過Intent直接傳遞。
 */
public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.mTvOpenNew).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent open = new Intent(MainActivity.this, SecondActivity.class);
                Person person = new Person();
                person.setName("一去二三裡");
                person.setAge();
                // 傳輸方式一,intent直接調用putExtra
                // public Intent putExtra(String name, Serializable value)
                open.putExtra("put_ser_test", person);
                // 傳輸方式二,intent利用putExtras(注意s)傳入bundle
                /**
                 Bundle bundle = new Bundle();
                 bundle.putSerializable("bundle_ser",person);
                 open.putExtras(bundle);
                 */
                startActivity(open);
            }
        });
        // 采用Parcelable的方式
        findViewById(R.id.mTvOpenThird).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent mTvOpenThird = new Intent(MainActivity.this,ThirdActivity.class);
                Pen tranPen = new Pen();
                tranPen.setColor("big red");
                tranPen.setSize();
                // public Intent putExtra(String name, Parcelable value)
                mTvOpenThird.putExtra("parcel_test",tranPen);
                startActivity(mTvOpenThird);
            }
        });
    }
}
                

ThirdActivity

package com.amqr.serializabletest;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
import com.amqr.serializabletest.entity.Pen;
/**
 * User: LJM
 * Date&Time: 2016-02-22 & 14:47
 * Describe: Describe Text
 */
public class ThirdActivity extends Activity{
    private TextView mTvThirdDate;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_third);
        mTvThirdDate = (TextView) findViewById(R.id.mTvThirdDate);
//        Intent intent = getIntent();
//        Pen pen = (Pen)intent.getParcelableExtra("parcel_test");
        Pen pen = (Pen)getIntent().getParcelableExtra("parcel_test");
        mTvThirdDate = (TextView) findViewById(R.id.mTvThirdDate);
        mTvThirdDate.setText("顔色:"+pen.getColor()+"\\n"
                            +"大小:"+pen.getSize());
    }
}                

連結:https://www.jianshu.com/p/a60b609ec7e7

(3)性能比較

    parcelable性能強于serializable

    (1)記憶體使用中強于後者

    (2)後者序列化操作時候,會産生大量臨時變量,進而導緻GC頻繁調用,性能下降

    (3)parcelable是以Ibinder為載體,記憶體上開銷較小。

    (4)讀寫資料的時候parcelable是直接讀寫,而serializable是通過IO流形式讀寫如硬碟上。

當然在将資料持久化的時候,如把資料寫入硬碟的時候,仍然需要使用serializable。

4.3建立一個簡單的AIDL執行個體

首先,我們就在AS裡面建立一個aidl檔案(ps:現在AS建aidl不要求和java包名相同了):

package aidl;
interface IMyInterface {
    String getInfor(String s);
}
           

可以看到,在這裡面我們就一個方法getInfor(String s),接受一個字元串參數,然後傳回一個字元串,相當的簡單。

接着你sync project一下就可以在app/generated/source/aidl/debug/aidl裡面發現由aidl檔案生成的java檔案了。點進去一看,可能你也被這個的類給吓住了,那現在我們就先不管它了。

然後就看看Service:

public class MyService extends Service { 

public final static String TAG = "MyService";

private IBinder binder = new IMyInterface.Stub() {
        @Override       
        public String getInfor(String s) throws RemoteException { 
            Log.i(TAG, s); 
            return "我是 Service 傳回的字元串"; 
       }
    };
@Overrid
public void onCreate() {
    super.onCreate(); 
    Log.i(TAG, "onCreat");    
}       
@Override    
public IBinder onBind(Intent intent) { 
    return binder;  
    }
}
           

       這裡我們寫了一個Service,看一下也比較簡單。先new了一IMyInterface.Stub()并把它向上轉型成了IBinder,最後在onBind方法中傳回回去。可能你注意到了,在IMyInterface.Stub()的内部我們重寫getInfor(String s) 方法,沒錯這就是我們 aidl檔案中定義的接口。 

       對了,因為我們希望看到的是跨程序通信,是以我們把MyService定義成新啟動一個程序:

<service
    android:name=".server.MyService"
    android:process="com.xu.remote" />
           

        定義為啟動在新程序中,隻需要在AndroidMainfest.xml中聲明是加上一個process屬性即可,不過這裡有兩個地方值得注意: 

1.元件預設的程序名就是包名; 

2.定義新的程序名的時候需要以包的形式(eg: com.xu.aidl)。

接着,我們繼續向下看:

public class MainActivity extends AppCompatActivity {
    public final static String TAG = "MainActivity";
    private IMyInterface myInterface;
    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            myInterface = IMyInterface.Stub.asInterface(service);
            Log.i(TAG, "連接配接Service 成功");
            try {
                String s = myInterface.getInfor("我是Activity傳來的字元串");
                Log.i(TAG, "從Service得到的字元串:" + s);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.e(TAG, "連接配接Service失敗");
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        startAndBindService();
    }
    private void startAndBindService() {
        Intent service = new Intent(MainActivity.this, MyService.class);
        //startService(service);
        bindService(service, serviceConnection, Context.BIND_AUTO_CREATE);
    }
}
           

在onCreate(Bundle savedInstanceState)中,我們調用了自己定義的一個方法startAndBindService(),這個方法裡面我們生成了一個Intent,然後 bindService了這個Intent傳入了三個參數分别是Intent、ServiceConnection、Flag。

Intent我們就不用說了,我們看看後面兩個參數: 

在Activity中,我們new了一個ServiceConnection并實作了他的兩個方法onServiceConnected、onServiceDisconnected。在onServiceConnected中我們通過IMyInterface.Stub.asInterface(service)把傳來的IBinder轉換成了我們定義的IMyInterface。然後我們調用了getInfor方法,傳遞了個字元串和擷取從MyService傳來的字元串,并且列印了Log。

對于我們傳的Context.BIND_AUTO_CREATE的意思就是說:如果綁定Service的時候,Service還沒有被建立,就建立它。

    大緻的實作流程就是,先定義一個AIDL接口,然後進行再service中的stub中實作接口中的方法,然後在client中實作通過ServiceConnection連接配接,并且在asInterface中調用方法。

    4.4 aidl中的接口解析

    Binder工作機制由用戶端、Binder、服務端組成,用戶端和服務端都是通過Binder進行交流。

    Stub類是繼承自Binder類,Stub類就是Binder的執行個體,在服務端一般會執行個體化一個Binder對象,用戶端在Service中綁定的時候可以擷取這個Stub(Binder)

通過asInterface()方法擷取它的執行個體對象。如果用戶端和服務端在同一程序下,那麼asInterface()将傳回Stub對象本身,否則傳回Stub.Prox對象。

也就是說asInterface()傳回的對象有兩種可能(實際上有三種,還有一種是null),Stub和Stub.Proxy。它們有什麼差別呢?

  1. 如果在同一個程序下的話,那麼asInterface()将傳回服務端的Stub對象本身,因為此時根本不需要跨進稱通信,那麼直接調用Stub對象的接口就可以了,傳回的實作就是服務端的Stub實作,也就是根本沒有跨程序通信;
  2. 如果不是同一個程序,那麼asInterface()傳回是Stub.Proxy對象,該對象持有着遠端的Binder引用,因為現在需要跨程序通信,是以如果調用Stub.Proxy的接口的話,那麼它們都将是IPC調用,它會通過調用transact方法去與服務端通信。
  3. Stub是服務端實作的存根,而Proxy則是Stub的代理。

5、多線程實作方法

    java實作多線程實作方式主要有四種:繼承Thread類、實作Runnable接口、實作Callable接口通過FutureTask包裝器建立Thread線程、使用ExecutorService、Callble、Future實作有傳回結果的多線程。

    (1)繼承Thread類建立線程

    Thread本質上是實作Runnable接口的一個執行個體,代表一個線程的執行個體。啟動線程的唯一方法就是通過Thread類的start()執行個體方法。start()方法是一個native方法,他啟動一個新縣城,并執行run()方法。直接繼承Thread,然後重寫run()方法啟動新線程即可執行自定義的run()方法。

    public class MyThread extends Thread{

        public void run(){

            //邏輯

        }

    }

MyThead myThead1 = new MyThread();

MyThread myThread2 = new MyThread();

myThread1.start();

myThread2.start();

(2)實作Runnable 接口建立線程

如果自己已經extends一個類,就無法直接extends Thread ,此時可以實作一個Runnable接口:

    public class MyThread extends OhterClass implements Runnable{

        public void run(){

            //邏輯

        }

    }

MyThread myThread = new MyThread();

Thread thread  = new Thread(myThread);

thread.start();

(3)實作Callable接口通過FutureTask包裝器來建立愛你Thread線程

     Callable接口定義:

public class SomeCallable<V> extends OtherClass implements Callable<V> {

    @Override
    public V call() throws Exception {
        // TODO Auto-generated method stub
        return null;
    }

}      
Callable<V> oneCallable = new SomeCallable<V>();   
//由Callable<Integer>建立一個FutureTask<Integer>對象:   
FutureTask<V> oneTask = new FutureTask<V>(oneCallable);   
//注釋:FutureTask<Integer>是一個包裝器,它通過接受Callable<Integer>來建立,它同時實作了Future和Runnable接口。 
  //由FutureTask<Integer>建立一個Thread對象:   
Thread oneThread = new Thread(oneTask);   
oneThread.start();   
//至此,一個線程就建立完成了。      

4、使用ExecutorService、Callable、Future實作有傳回結果的線程

      ExecutorService、Callable、Future三個接口實際上都是屬于Executor架構。傳回結果的線程是在JDK1.5中引入的新特征,有了這種特征就不需要再為了得到傳回值而大費周折了。而且自己實作了也可能漏洞百出。

      可傳回值的任務必須實作Callable接口。類似的,無傳回值的任務必須實作Runnable接口。

執行Callable任務後,可以擷取一個Future的對象,在該對象上調用get就可以擷取到Callable任務傳回的Object了。

注意:get方法是阻塞的,即:線程無傳回結果,get方法會一直等待。

再結合線程池接口ExecutorService就可以實作傳說中有傳回結果的多線程了。

下面提供了一個完整的有傳回結果的多線程測試例子,在JDK1.5下驗證過沒問題可以直接使用。代碼如下:

import java.util.concurrent.*;  
import java.util.Date;  
import java.util.List;  
import java.util.ArrayList;  
  
/** 
* 有傳回值的線程 
*/  
@SuppressWarnings("unchecked")  
public class Test {  
public static void main(String[] args) throws ExecutionException,  
    InterruptedException {  
   System.out.println("----程式開始運作----");  
   Date date1 = new Date();  
  
   int taskSize = 5;  
   // 建立一個線程池  
   ExecutorService pool = Executors.newFixedThreadPool(taskSize);  
   // 建立多個有傳回值的任務  
   List<Future> list = new ArrayList<Future>();  
   for (int i = 0; i < taskSize; i++) {  
    Callable c = new MyCallable(i + " ");  
    // 執行任務并擷取Future對象  
    Future f = pool.submit(c);  
    // System.out.println(">>>" + f.get().toString());  
    list.add(f);  
   }  
   // 關閉線程池  
   pool.shutdown();  
  
   // 擷取所有并發任務的運作結果  
   for (Future f : list) {  
    // 從Future對象上擷取任務的傳回值,并輸出到控制台  
    System.out.println(">>>" + f.get().toString());  
   }  
  
   Date date2 = new Date();  
   System.out.println("----程式結束運作----,程式運作時間【"  
     + (date2.getTime() - date1.getTime()) + "毫秒】");  
}  
}  
  
class MyCallable implements Callable<Object> {  
private String taskNum;  
  
MyCallable(String taskNum) {  
   this.taskNum = taskNum;  
}  
  
public Object call() throws Exception {  
   System.out.println(">>>" + taskNum + "任務啟動");  
   Date dateTmp1 = new Date();  
   Thread.sleep(1000);  
   Date dateTmp2 = new Date();  
   long time = dateTmp2.getTime() - dateTmp1.getTime();  
   System.out.println(">>>" + taskNum + "任務終止");  
   return taskNum + "任務傳回運作結果,目前任務時間【" + time + "毫秒】";  
}  
}        

代碼說明:

上述代碼中Executors類,提供了一系列工廠方法用于建立線程池,傳回的線程池都實作了ExecutorService接口。

public static ExecutorService newFixedThreadPool(int nThreads) 

建立固定數目線程的線程池。

public static ExecutorService newCachedThreadPool() 

建立一個可緩存的線程池,調用execute 将重用以前構造的線程(如果線程可用)。如果現有線程沒有可用的,則建立一個新線程并添加到池中。終止并從緩存中移除那些已有 60 秒鐘未被使用的線程。

public static ExecutorService newSingleThreadExecutor() 

建立一個單線程化的Executor。

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) 

建立一個支援定時及周期性的任務執行的線程池,多數情況下可用來替代Timer類。

ExecutoreService提供了submit()方法,傳遞一個Callable,或Runnable,傳回Future。如果Executor背景線程池還沒有完成Callable的計算,這調用傳回Future對象的get()方法,會阻塞直到計算完成。

參考:https://www.cnblogs.com/felixzh/p/6036074.html

https://blog.csdn.net/baidu_29094221/article/details/78852998

https://blog.csdn.net/yan8024/article/details/6444368

https://blog.csdn.net/aboy123/article/details/38307539/

https://blog.csdn.net/wangchunlei123/article/details/51345130

https://www.cnblogs.com/yezhennan/p/5527506.html

大緻就寫到這裡吧,知識越擴充越多,本人技術小白一個,有錯誤地地方,多多指點。