Java編程思想——併發

前言

對於某些問題,如果能夠並行的執行程序中的多個部分,則回變得非常方便甚至必要,這些部分要麼看起來是併發執行,要麼是在多處理環境下同時執行。並行編輯可以使程序執行速度得到極大提高,或者為設計某些類型的程序提供更易用的模型。當並行執行的任務彼此開始產生互相干涉時,實際的併發問題就發生了。

一、併發的多面性

併發解決的問題答題上可以分為“速度”和“設計可管理新”兩種。

1.更快的執行

想要更快的執行,需要多處理器,併發是用於多處理器編程的基本工具。這是使用強有力的多處理器Web服務器的常見情況,在為每個請求分配一個線程的程序中,它可以將大量的用戶請求分佈到多個CPU上。

當併發運行在單處理器時,開銷可能要比順序執行開銷大,因為增加了上下文切換的代價。但是阻塞使得問題變得不同:如果程序中的某個任務因為該程序控制範圍之外的某些條件(如:I/O)而導致不能繼續執行,那麼這個任務線程阻塞了。如果沒有併發,則整個程序都將停止下來。因此,如果沒有任務會阻塞,在單線程處理器機器上使用併發就沒有任何意義。單線程併發一般使用在窗口操作。

Java所使用的這種併發系統會共享諸如內存和I/O這樣的資源,因此編寫多線程程序最基本的困難在於協調不同線程驅動的任務之間對這些資源的使用,以使得這些資源不會同時被多個任務訪問。

2.改進代碼設計

簡單舉個例子吧,遊戲裡面多個npc,各自走各自的。

二、基本的線程機制

併發編程是我們可以將程序劃分為多個分離的、獨立運行的任務。通過多線程機制,這些獨立任務中每一個都將由執行線程來驅動。一個線程就是在進程中的一個單一的順序控制流,因此,單進程可以擁有多個併發執行的任務,但是程序使得每個人物都想有自己的CPU。其底層機制是切分CPU時間。

1.定義任務

線程可以驅動任務,因此你需要一種描述任務的方式,這可以由Runnable接口來提供。要想定義任務,只需實現Runnable接口並編寫run()方法,使得該任務可以執行你的命令。

public class RunnableDemo implements Runnable {
int i =100;
@Override
public void run() {
while (i-->0){
Thread.yield();
}
}
}

任務的run()方法總會以循環的形式使任務一直進行下去,在run()中對靜態方法Thread.yield()的調用是對線程調度器(Java線程機制的一部分,可以將CPU從一個線程轉移給另一個線程)的一種建議,它聲明:“我已經完成生命週期中最重要的部分,此刻是切換給其他任務執行一段時間的大好時機。

當Runnable導出一個類時,它必須具有run()方法,但是這個方法並無特殊之處——它不會產生任何內在的線程能力。要實現縣城行為,你必須顯式地將一個任務附著到線程了。

2.Thread類

將Runnable對象轉變為工作任務的傳統方式是把它提交給一個Thread構造器:

 public static void main(String[] args) {
Thread t = new Thread(new RunnableDemo());
t.start();

     //其他方法
}

Thread構造器只需要一個Runnable對象。調用Thread對象的start()方法為該線程執行必須的初始化操作,然後調用Runnable的run()方法,以便在這個新線程中啟動該任務。start()方法實際上,產生的是對Runnable.run()的調用。程序會同時運行兩個方法,main()裡面的其他方法和Runnable.run()是程序中與其他線程“同時”執行代碼。

3.使用Executor

執行器(Excutor)將為你管理Thread對象,簡化了併發編程。相當於中介。但是由於一下原因不是很推薦

Java編程思想——併發

推薦:ThreadPoolExecutor使用 。

4.從任務中產生返回值

Runnable是執行工作的獨立任務,但是它不返回任何值。如果希望任務中返回值那麼應當實現Callable接口。Callable具有泛型,它的類型參數標識從call()方法中返回的值,並且必須使用ExectorService.submit()方法調用:

public class CallableDemo {
public static void main(String[] args) {
ExecutorService executorService = new ThreadPoolExecutor(5, 10, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1));
List<future>> results = new ArrayList<>();
for (int i = 0; i < 10; i++) {
results.add(executorService.submit(new TaskWithResult(i)));
}
for (Future<string> fs : results) {
try {
//得到返回值
System.out.println(fs.get());
} catch (Exception e) {
e.printStackTrace();
} finally {
executorService.shutdown();
}
}
}
}
class TaskWithResult implements Callable<string> {
private int id;
TaskWithResult(int id) {
this.id = id;
}
@Override
public String call() {
return "result of TaskWithResult" + id;
}
}
/<string>/<string>/<future>

5.休眠

影響任務行為的一種簡單方法是調用sleep(),這將使任務中止執行對應的時間。

 @Override
public void run() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

6.優先級

線程的優先級將該線程的重要性傳遞給調度器,調度器傾向於讓優先權最高的線程先執行。但這並不意味著優先級低的線程得不到執行(優先權高的等待不會導致死鎖),優先權低的線程僅僅是執行頻率較低。在絕大多數時間裡,所有程序都應該是默認優先級,試圖操作線程優先級通常是一種錯誤。

 @Override
public void run() {
Thread.currentThread().setPriority(Thread.MIN_PRIORITY );
Thread.currentThread().getPriority();
}

最好在run方法裡面設置優先級,而且最好就用那三種常用的級別 :

Thread.MAX_PRIORITY
Thread.NORM_PRIORITY
Thread.MIN_PRIORITY

7.讓步

當工作做了一段時間可以讓別的線程使用cpu了。此時可以使用Thread.yield()給線程調度一個暗示(只是一個暗示,不一定被採納)。

8.後臺線程

所謂後臺線程,是指在程序運行時,在後臺提供一種通用服務的線程,並且這種線程並不屬於程序中不可或缺的部分。當所有非後臺線程結束時,程序也就終止了,同時會殺死進程中所有的後臺線程。

設置後臺線程:

 public static void main(String[] args) {
Thread t = new Thread(new RunnableDemo());
//這句設置線程為後臺線程
t.setDaemon(true);
t.start();
}

9.編碼的變體

在非常簡單的情況下,你可能會希望使用直接哦那個Thread繼承這種可替換的方式:

public class SimpleThrad extends Thread {
private int countDown = 5;
/**
* 依然需要實現run方法
*/
@Override
public void run() {
while (true) {
System.out.println(this);
if (--countDown == 0) {
return;
}
}
}
}

但是不提倡還是提倡使用ThreadPoolExecutor實現線程管理。

10.術語

從上面的各種情況中你可以看到實際你沒有對Thread的控制權。你創建任務,並通過某種方式將一個線程附著到任務上,以使得這個線程可以驅動任務。在Java中Thread類自身不執行任何操作,它只是驅動賦予給他的任務,將任務和線程區分開能讓你更好的理解線程。

11.加入一個線程

一個線程可以在其他線程上調用join()方法,其效果是等待一段時間直到第二線程結束才繼續執行。如果某個線程在另一個線程t上調用t.join(),此線程將被掛起,知道目標線程t結束才恢復。

也可也在join()加上超時參數(毫秒),使得目標函數在參數時間外還未結束,join()方法依舊能返回。對join()方法的調用可以被中斷,做法是在調用線程上調用interrppt()方法,並加try-catch。這裡不舉例子了因為在使用過程中CycliBarrier要比join更好。

Java編程思想——併發


分享到:


相關文章: