多線程并發(fā)詳解

一、Java 線程實現(xiàn)/創(chuàng)建方式

注意:

? 新建的線程不會自動開始運行,必須通過start( )方法啟動

? 不能直接調(diào)用run()來啟動線程,這樣run()將作為一個普通方法立即執(zhí)行,執(zhí)行完畢前其他線程無法并發(fā)執(zhí)行

? Java程序啟動時,會立刻創(chuàng)建主線程,main就是在這個線程上運行。當不再產(chǎn)生新線程時,程序是單線程的

1.1 繼承Thread 類

Thread 類本質上是實現(xiàn)了 Runnable 接口的一個實例,代表一個線程的實例。啟動線程的唯一方法就是通過 Thread 類的 start()實例方法。start()方法是一個 native 方法,它將啟動一個新線程,并執(zhí)行 run()方法。

? 優(yōu)勢:編寫簡單

? 劣勢:無法繼承其它父類

1.1.1 ****創(chuàng)建:繼承Thread+重寫run

  1.1.2 啟動:創(chuàng)建子類對象+調(diào)用start

public class StartThread extends Thread{

//線程入口點

@Override

public void run() {

for(int i=0;i<10;i++) {

System.out.println("listen music");

}

}

public static void main(String[] args) {

//創(chuàng)建子類對象

StartThread st=new StartThread();

//調(diào)用start方法

st.start();//開啟新線程交于cpu決定執(zhí)行順序

for(int i=0;i<10;i++) {

System.out.println("coding");

}

}

}

1.2 實現(xiàn)runnable接口

如果自己的類已經(jīng) extends 另一個類,就無法直接 extends Thread,此時,可以實現(xiàn)一個Runnable 接口。

? 優(yōu)勢:可以繼承其它類,多線程可共享同一個Runnable對象

? 劣勢:編程方式稍微復雜,如果需要訪問當前線程,需要調(diào)用Thread.currentThread()方法

1.2.1 創(chuàng)建:實現(xiàn)runnable接口+重寫run

   1.2.2 啟動:創(chuàng)建實現(xiàn)類對象+Thread類對象+調(diào)用start

public class StartRun implements Runnable{

//線程入口點

@Override

public void run() {

for(int i=0;i<10;i++) {

System.out.println("listen music");

}

}

public static void main(String[] args) {

//創(chuàng)建實現(xiàn)類對象

StartRun st=``new` `StartRun();

//創(chuàng)建代理類對象<br>         
 //啟動 MyThread,需要首先實例化一個 Thread,并傳入自己的 MyThread 實例:`
Thread t=``new` `Thread(st);

//事實上,當傳入一個 Runnable target 參數(shù)給 Thread 后,Thread 的 run()方法就會調(diào)用target.run()

//調(diào)用start方法

t.start();//開啟新線程交于cpu決定執(zhí)行順序`

//匿名法

//      new Thread(new StartRun()).start();

for(int i=0;i<10;i++) {

System.out.println("coding");

}

}

}

1.3 實現(xiàn)Callable接口

有返回值的任務必須實現(xiàn) Callable 接口,類似的,無返回值的任務必須 Runnable 接口。執(zhí)行Callable 任務后,可以獲取一個 Future 的對象,在該對象上調(diào)用 get 就可以獲取到 Callable 任務返回的 Object 了,再結合線程池接口 ExecutorService 就可以實現(xiàn)傳說中有返回結果的多線程了。

? 與實行Runnable相比, Callable功能更強大些

? 方法不同

? 可以有返回值,支持泛型的返回值

? 可以拋出異常

? 需要借助FutureTask,比如獲取返回結果

Future接口

? 可以對具體Runnable、Callable任務的執(zhí)行結果進行取消、查詢是否完成、獲取結果等。

? FutrueTask是Futrue接口的唯一的實現(xiàn)類

? FutureTask 同時實現(xiàn)了Runnable, Future接口。它既可以作為Runnable被線程執(zhí)行,又可以作為Future得到Callable的返回值

//創(chuàng)建一個線程池

ExecutorService pool = Executors.newFixedThreadPool(taskSize);

// 創(chuàng)建多個有返回值的任務`

List<Future> list = new ArrayList<Future>();

for (int i = 0; i < taskSize; i++) {

Callable c = ``new` `MyCallable(i +" ");

// 執(zhí)行任務并獲取 Future 對象

Future f = pool.submit(c);

list.add(f);

}

// 關閉線程池

pool.shutdown();

// 獲取所有并發(fā)任務的運行結果`

for (Future f : list) {

// 從 Future 對象上獲取任務的返回值,并輸出到控制臺

System.out.println(``"res:"` `+ f.get().toString());

}

1.4 基于線程池的方式

線程和數(shù)據(jù)庫連接這些資源都是非常寶貴的資源。那么每次需要的時候創(chuàng)建,不需要的時候銷毀,是非常浪費資源的。那么我們就可以使用緩存的策略,也就是使用線程池。

// 創(chuàng)建線程池

ExecutorService threadPool = Executors.newFixedThreadPool(10);

while(true) {

threadPool.execute(new Runnable() { // 提交多個線程任務,并執(zhí)行

@Override

public void run() {

System.out.println(Thread.currentThread().getName() + " is running ..");

try {

Thread.sleep(3000);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

});

}

}               `

二、四種線程池

Java 里面線程池的頂級接口是 Executor,但是嚴格意義上講 Executor 并不是一個線程池,而只是一個執(zhí)行線程的工具。真正的線程池接口是 ExecutorService。

image

2.1 newCachedThreadPool

創(chuàng)建一個可根據(jù)需要創(chuàng)建新線程的線程池,但是在以前構造的線程可用時將重用它們。對于執(zhí)行很多短期異步任務的程序而言,這些線程池通??商岣叱绦蛐阅堋?strong>調(diào)用 execute 將重用以前構造的線程(如果線程可用)。如果現(xiàn)有線程沒有可用的,則創(chuàng)建一個新線程并添加到池中。終止并從緩存中移除那些已有 60 秒鐘未被使用的線程。因此,長時間保持空閑的線程池不會使用任何資源。

2.2 newFixedThreadPool

創(chuàng)建一個可重用固定線程數(shù)的線程池,以共享的無界隊列方式來運行這些線程。在任意點,在大多數(shù) Threads 線程會處于處理任務的活動狀態(tài)。如果在所有線程處于活動狀態(tài)時提交附加任務,則在有可用線程之前,附加任務將在隊列中等待。如果在關閉前的執(zhí)行期間由于失敗而導致任何線程終止,那么一個新線程將代替它執(zhí)行后續(xù)的任務(如果需要)。在某個線程被顯式地關閉之前,池中的線程將一直存在

2.3 newScheduledThreadPool

創(chuàng)建一個線程池,它可安排在給定延遲后運行命令或者定期地執(zhí)行。

ScheduledExecutorService scheduledThreadPool= Executors.newScheduledThreadPool(3);

scheduledThreadPool.schedule(newRunnable(){

@Override

public void run() {

System.out.println("延遲三秒");

}

}, 3, TimeUnit.SECONDS);

scheduledThreadPool.scheduleAtFixedRate(newRunnable(){

@Override

public void run() {

System.out.println延遲 1 秒后每三秒執(zhí)行一次");

},1,3,TimeUnit.SECONDS);

2.4 newSingleThreadExecutor

Executors.newSingleThreadExecutor()返回一個線程池(這個線程池只有一個線程),這個線程池可以在線程死后(或發(fā)生異常時)重新啟動一個線程來替代原來的線程繼續(xù)執(zhí)行下去!

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容