日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

多线程的几种实现方式

發(fā)布時(shí)間:2023/12/20 编程问答 53 豆豆
生活随笔 收集整理的這篇文章主要介紹了 多线程的几种实现方式 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

先上總結(jié):

1.使用實(shí)現(xiàn)多線程有四種方式:①繼承Thread類(lèi);②實(shí)現(xiàn)Runnable接口;③使用Callable和FutureTask實(shí)現(xiàn)有返回值的多線程;④使用ExecutorService和Executors工具類(lèi)實(shí)現(xiàn)線程池(如果需要線程的返回值,需要在線程中實(shí)現(xiàn)Callable和Future接口)

2.繼承Thread類(lèi)的優(yōu)點(diǎn):簡(jiǎn)單,且只需要實(shí)現(xiàn)父類(lèi)的run方法即可(start方法中含有run方法,會(huì)創(chuàng)建一個(gè)新的線程,而run是執(zhí)行當(dāng)前線程)。缺點(diǎn)是:Java的單繼承,如果對(duì)象已經(jīng)繼承了其他的類(lèi)則不能使用該方法。且不能獲取線程的返回值

3.實(shí)現(xiàn)Runnable接口優(yōu)點(diǎn):簡(jiǎn)單,實(shí)現(xiàn)Runnable接口必須實(shí)現(xiàn)run方法。缺點(diǎn):創(chuàng)建一個(gè)線程就必須創(chuàng)建一個(gè)Runnable的實(shí)現(xiàn)類(lèi),且不能獲取線程的返CallabTask優(yōu)點(diǎn):可以獲取多線程的返回值。缺點(diǎn):每個(gè)多線程都需要?jiǎng)?chuàng)建一個(gè)Callable的實(shí)現(xiàn)類(lèi)

4.線程池ExecutorService和工具類(lèi)Executors優(yōu)點(diǎn):可以根據(jù)實(shí)際情況創(chuàng)建線程數(shù)量,且只需要?jiǎng)?chuàng)建一個(gè)線程池即可,也能夠通過(guò)Callable和Future接口得到線程的返回值,程序的執(zhí)行時(shí)間與線程的數(shù)量緊密相關(guān)。缺點(diǎn):需要手動(dòng)銷(xiāo)毀該線程池(調(diào)用shutdown方法)。

盡量不要使用 繼承Thread類(lèi) 和 實(shí)現(xiàn)Runnable接口;盡量使用線程池。否則項(xiàng)目導(dǎo)出都是線程。

在上代碼:

package com.swain.programmingpearls.thread;import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.FutureTask;/*** thread 的幾種實(shí)現(xiàn)*/ public class threadTest {public static void main (String[] args) {//繼承threadExtendsThread extendsThread = new ExtendsThread();extendsThread.start();//實(shí)現(xiàn)runnableThread runThread = new Thread(new AchieveRunnable());runThread.start();//調(diào)用callable 可以有返回值 可以捕獲異常Callable<String> tc = new TestCallable();FutureTask<String> task = new FutureTask<String>(tc);new Thread(task).start();try {System.out.println(task.get());//獲取返回值} catch (InterruptedException | ExecutionException e) {e.printStackTrace();}//runable 匿名內(nèi)部類(lèi)方式new Thread(new Runnable() {@Overridepublic void run() {System.out.println("實(shí)現(xiàn)Runnable 匿名內(nèi)部類(lèi)方式:" + Thread.currentThread().getName());}}).start();//runnable Lamda表達(dá)式new Thread(()->{for (int i = 0; i < 5; i++) {System.out.println("Lamda表達(dá)式:" + i);}}).start();System.out.println("主線程");//創(chuàng)建線程池ExecutorService executorService = Executors.newFixedThreadPool(2);for(int i = 0; i<5; i++){AchieveRunnable achieveRunnable = new AchieveRunnable();try {Thread.sleep(1000);} catch (InterruptedException e) {}executorService.execute(achieveRunnable);}//關(guān)閉線程池executorService.shutdown();}/*** 繼承thread類(lèi)*/public static class ExtendsThread extends Thread {public void run(){System.out.println("方法一 繼承Thread線程:" + Thread.currentThread().getName());}}/*** 實(shí)現(xiàn)runnable*/public static class AchieveRunnable implements Runnable {@Overridepublic void run() {System.out.println("方法二 實(shí)現(xiàn)Runnable:" + Thread.currentThread().getName());}}/*** 通過(guò)Callable和FutureTask創(chuàng)建線程*/public static class TestCallable implements Callable<String> {@Overridepublic String call() throws Exception {System.out.println("方法三 實(shí)現(xiàn)callable:" + Thread.currentThread().getName());return "我是callable的返回";}} }運(yùn)行結(jié)果: 方法一 繼承Thread線程:Thread-0 方法二 實(shí)現(xiàn)Runnable:Thread-1 方法三 實(shí)現(xiàn)callable:Thread-2 我是callable的返回 實(shí)現(xiàn)Runnable 匿名內(nèi)部類(lèi)方式:Thread-3 主線程 Lamda表達(dá)式:0 Lamda表達(dá)式:1 Lamda表達(dá)式:2 Lamda表達(dá)式:3 Lamda表達(dá)式:4 方法二 實(shí)現(xiàn)Runnable:pool-1-thread-1 方法二 實(shí)現(xiàn)Runnable:pool-1-thread-2 方法二 實(shí)現(xiàn)Runnable:pool-1-thread-1 方法二 實(shí)現(xiàn)Runnable:pool-1-thread-2 方法二 實(shí)現(xiàn)Runnable:pool-1-thread-1

?

關(guān)于Concurrent包

concurrent包是在AQS的基礎(chǔ)上搭建起來(lái)的,AQS提供了一種實(shí)現(xiàn)阻塞鎖和一系列依賴(lài)FIFO等待隊(duì)列的同步器的框架。

線程池參數(shù)

我們常用的主要有newSingleThreadExecutor、newFixedThreadPool、newCachedThreadPool、調(diào)度等,使用Executors工廠類(lèi)創(chuàng)建。

newSingleThreadExecutor可以用于快速創(chuàng)建一個(gè)異步線程,非常方便。而newCachedThreadPool永遠(yuǎn)不要用在高并發(fā)的線上環(huán)境,它用的是無(wú)界隊(duì)列對(duì)任務(wù)進(jìn)行緩沖,可能會(huì)擠爆你的內(nèi)存。

我習(xí)慣性自定義ThreadPoolExecutor,也就是參數(shù)最全的那個(gè)。

假如我的任務(wù)可以預(yù)估,corePoolSize,maximumPoolSize一般都設(shè)成一樣大的,然后存活時(shí)間設(shè)的特別的長(zhǎng)??梢员苊饩€程頻繁創(chuàng)建、關(guān)閉的開(kāi)銷(xiāo)。I/O密集型和CPU密集型的應(yīng)用線程開(kāi)的大小是不一樣的,一般I/O密集型的應(yīng)用線程就可以開(kāi)的多一些。

threadFactory我一般也會(huì)定義一個(gè),主要是給線程們起一個(gè)名字。這樣,在使用jstack等一些工具的時(shí)候,能夠直觀的看到我所創(chuàng)建的線程。

監(jiān)控

高并發(fā)下的線程池,最好能夠監(jiān)控起來(lái)。可以使用日志、存儲(chǔ)等方式保存下來(lái),對(duì)后續(xù)的問(wèn)題排查幫助很大。

通常,可以通過(guò)繼承ThreadPoolExecutor,覆蓋beforeExecute、afterExecute、terminated方法,達(dá)到對(duì)線程行為的控制和監(jiān)控。

阻塞隊(duì)列

阻塞隊(duì)列會(huì)對(duì)當(dāng)前的線程進(jìn)行阻塞。當(dāng)隊(duì)列中有元素后,被阻塞的線程會(huì)自動(dòng)被喚醒,這極大的提高的編碼的靈活性,非常方便。在并發(fā)編程中,一般推薦使用阻塞隊(duì)列,這樣實(shí)現(xiàn)可以盡量地避免程序出現(xiàn)意外的錯(cuò)誤。阻塞隊(duì)列使用最經(jīng)典的場(chǎng)景就是socket數(shù)據(jù)的讀取、解析,讀數(shù)據(jù)的線程不斷將數(shù)據(jù)放入隊(duì)列,解析線程不斷從隊(duì)列取數(shù)據(jù)進(jìn)行處理。

ArrayBlockingQueue對(duì)訪問(wèn)者的調(diào)用默認(rèn)是不公平的,我們可以通過(guò)設(shè)置構(gòu)造方法參數(shù)將其改成公平阻塞隊(duì)列。

LinkedBlockingQueue隊(duì)列的默認(rèn)最大長(zhǎng)度為Integer.MAX_VALUE,這在用做線程池隊(duì)列的時(shí)候,會(huì)比較危險(xiǎn)。

SynchronousQueue是一個(gè)不存儲(chǔ)元素的阻塞隊(duì)列。每一個(gè)put操作必須等待一個(gè)take操作,否則不能繼續(xù)添加元素。隊(duì)列本身不存儲(chǔ)任何元素,吞吐量非常高。對(duì)于提交的任務(wù),如果有空閑線程,則使用空閑線程來(lái)處理;否則新建一個(gè)線程來(lái)處理任務(wù)”。它更像是一個(gè)管道,在一些通訊框架中(比如rpc),通常用來(lái)快速處理某個(gè)請(qǐng)求,應(yīng)用較為廣泛。

DelayQueue是一個(gè)支持延時(shí)獲取元素的無(wú)界阻塞隊(duì)列。放入DelayQueue的對(duì)象需要實(shí)現(xiàn)Delayed接口,主要是提供一個(gè)延遲的時(shí)間,以及用于延遲隊(duì)列內(nèi)部比較排序。這種方式通常能夠比大多數(shù)非阻塞的while循環(huán)更加節(jié)省cpu資源。

另外還有PriorityBlockingQueue和LinkedTransferQueue等,根據(jù)字面意思就能猜測(cè)它的用途。在線程池的構(gòu)造參數(shù)中,我們使用的隊(duì)列,一定要注意其特性和邊界。比如,即使是最簡(jiǎn)單的newFixedThreadPool,在某些場(chǎng)景下,也是不安全的,因?yàn)樗褂昧藷o(wú)界隊(duì)列。

CountDownLatch

假如有一堆接口A-Y,每個(gè)接口的耗時(shí)最大是200ms,最小是100ms。

我的一個(gè)服務(wù),需要提供一個(gè)接口Z,調(diào)用A-Y接口對(duì)結(jié)果進(jìn)行聚合。接口的調(diào)用沒(méi)有順序需求,接口Z如何在300ms內(nèi)返回這些數(shù)據(jù)?

此類(lèi)問(wèn)題典型的還有賽馬問(wèn)題,只有通過(guò)并行計(jì)算才能完成問(wèn)題。歸結(jié)起來(lái)可以分為兩類(lèi):

  • 實(shí)現(xiàn)任務(wù)的并行性
  • 開(kāi)始執(zhí)行前等待n個(gè)線程完成任務(wù)

CountDownLatch是通過(guò)一個(gè)計(jì)數(shù)器來(lái)實(shí)現(xiàn)的,計(jì)數(shù)器的初始值為線程的數(shù)量。每當(dāng)一個(gè)線程完成了自己的任務(wù)后,計(jì)數(shù)器的值就會(huì)減1。當(dāng)計(jì)數(shù)器值到達(dá)0時(shí),它表示所有的線程已經(jīng)完成了任務(wù),然后在閉鎖上等待的線程就可以恢復(fù)執(zhí)行任務(wù)。

CyclicBarrier與其類(lèi)似,可以實(shí)現(xiàn)同樣的功能。不過(guò)在日常的工作中,使用CountDownLatch會(huì)更頻繁一些。

信號(hào)量

Semaphore雖然有一些應(yīng)用場(chǎng)景,但大部分屬于炫技,在編碼中應(yīng)該盡量少用。

信號(hào)量可以實(shí)現(xiàn)限流的功能,但它只是常用限流方式的一種。其他兩種是漏桶算法、令牌桶算法

Lock && Condition

在Java中,對(duì)于Lock和Condition可以理解為對(duì)傳統(tǒng)的synchronized和wait/notify機(jī)制的替代。concurrent包中的許多阻塞隊(duì)列,就是使用Condition實(shí)現(xiàn)的。

End

不管是wait、notify,還是同步關(guān)鍵字或者鎖,能不用就不用,因?yàn)樗鼈儠?huì)引發(fā)程序的復(fù)雜性。最好的方式,是直接使用concurrent包所提供的機(jī)制,來(lái)規(guī)避一些編碼方面的問(wèn)題。

總結(jié)

以上是生活随笔為你收集整理的多线程的几种实现方式的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。