歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Java並發操作——基本的線程機制

Java並發操作——基本的線程機制

日期:2017/3/1 9:39:05   编辑:Linux編程

1)定義任務

線程可以驅動任務,因此需要一種描述任務的方式,這可以由Runnable接口來提供(有的地方比如Swing2中經常將其叫做可運行對象)。要想定義任務,只需要實現Runnable接口並編寫run()方法,使得該任務執行命令。 但是此時呢,這個run()方法跟一個普通方法一樣,並沒有什麼特殊的地方,不會產生任何內在的線程能力。要實現線程行為,我們必須在使用的時候顯示的將一個任務附著到現場上。

2)Thread類

要想Runnable對象要想具有線程行為,傳統的方式就是把它提交給一個Thread構造器。然後調用Thread類的start()方法為該線程執行必須的初始化操作,繼而調用Runnable的run()方法。

下面的代碼簡單演示Thread的用法

public class LiftOff implements Runnable {

public int countDown =10;

private static int taskCount = 0;

private final int id = taskCount++;

public LiftOff() {
System.out.println("默認構造函數");
}

public LiftOff(int countDown) {
System.out.println("非默認的構造函數");
this.countDown = countDown;
}

public String getStatus() {
return "#" + id + "(" + (countDown > 0 ? countDown : "LiftOff") + ").";
}

@Override
public void run() {
while(countDown-->0) {
System.out.print(getStatus());
Thread.yield();
}
}

}

import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class ThreadTestWithExecutor {

private static final int count = 5;

/**
* @param args
*/
public static void main(String[] args) {
//1.什麼都不加。
for(int i=0;i<count;i++) {
Thread t = new Thread(new LiftOff());
t.start();
}
System.out.println("執行完畢!");
}

}

第一次執行結果

默認構造函數
默認構造函數
默認構造函數
默認構造函數
默認構造函數
執行完畢!
#0(9).#0(8).#0(7).#1(9).#3(9).#2(9).#4(9).#0(6).#2(8).#4(8).#0(5).#2(7).#4(7).#0(4).#2(6).#4(6).#3(8).#2(5).#4(5).#2(4).#2(3).#4(4).#1(8).#3(7).#2(2).#1(7).#3(6).#2(1).#1(6).#3(5).#0(3).#2(LiftOff).#1(5).#3(4).#4(3).#1(4).#3(3).#0(2).#4(2).#1(3).#3(2).#0(1).#3(1).#1(2).#4(1).#1(1).#3(LiftOff).#0(LiftOff).#1(LiftOff).#4(LiftOff)

第二次執行結果

默認構造函數
默認構造函數
默認構造函數
默認構造函數
默認構造函數
執行完畢!
#1(9).#3(9).#1(8).#3(8).#1(7).#3(7).#1(6).#3(6).#1(5).#3(5).#1(4).#3(4).#1(3).#3(3).#1(2).#3(2).#1(1).#3(1).#1(LiftOff).#3(LiftOff).#0(9).#2(9).#4(9).#0(8).#2(8).#4(8).#0(7).#2(7).#4(7).#0(6).#2(6).#4(6).#0(5).#2(5).#4(5).#0(4).#2(4).#4(4).#0(3).#2(3).#4(3).#0(2).#2(2).#4(2).#0(1).#2(1).#4(1).#0(LiftOff).#2(LiftOff).#4(LiftOff).

從上述兩次的執行結果中,我們可以得到以下結論:

(1)多線程的執行不是按照順序執行的,各個線程之間相互交叉。
#0(9).#0(8).#0(7).#1(9).#3(9).#2(9) 說明可Runnable0還沒執行完,Runnable2 就執行了。

(2)start()方法看起來是產生了一個對長期運行方法的調用(即run方法),但是從實際的輸出情況來看,start()方法迅速的返回了,因為“執行完畢”在run()方法執行完畢之前就已經輸出了。實際上,我們產生的是對Runnable.run()方法的調用,並且這個方法還沒有完成,但是因為這個方法是由不同的線程執行的,因此我們仍然可以執行main()線程中的其他操作(當然這種能力並不局限於main()線程,任何線程都可以啟動另一個線程)。因此這時候程序會同時運行兩個方法、

(3)比較兩次執行結果可以發現,兩次執行的結果不一樣。這是因為線程調度機制是非確定的。

3)使用Exexutor

在Java SE5的java.util.concurrent包中的執行器(Executor)將為我們管理Thread對象,從而簡化了並發編程。

下面的代碼就列舉了幾種不同Executor的執行情況:

public class LiftOff implements Runnable {

public int countDown =10;

private static int taskCount = 0;

private final int id = taskCount++;

public LiftOff() {
System.out.println("默認構造函數");
}

public LiftOff(int countDown) {
System.out.println("非默認的構造函數");
this.countDown = countDown;
}

public String getStatus() {
return "#" + Thread.currentThread().getId() + "(" + (countDown > 0 ? countDown : "LiftOff") + ").";
}

@Override
public void run() {
while(countDown-->0) {
System.out.print(getStatus());
Thread.yield();
}
}

}

import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
* CachedThreadPool
*/
public class ThreadTestWithExecutor {


private static final int count = 5;

/**
* @param args
*/
public static void main(String[] args) {

ExecutorService exec = Executors.newCachedThreadPool();
for(int i=0;i<count;i++) {
exec.execute(new LiftOff());
}
exec.shutdown();


//3.使用FixThreadPool
ExecutorService exec2 = Executors.newFixedThreadPool(3);
for(int i=0;i<count;i++) {
exec2.execute(new LiftOff());
}
exec2.shutdown();

//4.SingleThreadExecutor
ExecutorService exec3 = Executors.newSingleThreadExecutor();
for(int i=0;i<count;i++) {
exec3.execute(new LiftOff());
}
exec.shutdown();
System.out.println("執行完畢!");

}

}

這幾段代碼,都很簡單,但是有幾點需要說明

1)shutdown()方法的調用可以防止新任務被提交給這個Executor,當前線程(在上述代碼中就是驅動main()的線程)將繼續運行在shutdown()被調用之前提交的所有任務。

換句說,雖然是shutdown了,但是當前在執行器中的線程仍然會繼續執行。

2)FixedThreadPool可以一次性預先執行代價高昂的線程分配(因而就可以限制線程的數量了)。這樣會節省時間,因為你不用為每個任務都固定的付出創建線程的時間(記住,雖然每次new了一個Runnable對象,但是並不是每個Runnable對象都會new一次Thread.),直接從線程池中取線程即可。 但是,我們一定要記住一點,在任何線程池中,現有線程在可能的情況下, 都會被復用。

3)SingleThreadExecutor就像是線程數量為1的FixedThreadPool。這時候如果向其提交了多個任務,這些任務就會排隊。每個任務都會在下一個任務開始之前運行結束。所有的任務將使用相同的線程。

大話設計模式(帶目錄完整版) PDF+源代碼 http://www.linuxidc.com/Linux/2014-08/105152.htm

Java中介者設計模式 http://www.linuxidc.com/Linux/2014-07/104319.htm

Java 設計模式之模板方法開發中應用 http://www.linuxidc.com/Linux/2014-07/104318.htm

設計模式之 Java 中的單例模式(Singleton) http://www.linuxidc.com/Linux/2014-06/103542.htm

Copyright © Linux教程網 All Rights Reserved