国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專(zhuān)欄INFORMATION COLUMN

重拾JAVA線程之獲取另一個(gè)線程的返回

liuchengxu / 1584人閱讀

摘要:它將管理線程的創(chuàng)建銷(xiāo)毀和復(fù)用,盡最大可能提高線程的使用效率。如果我們?cè)诹硪粋€(gè)線程中需要使用這個(gè)結(jié)果,則這個(gè)線程會(huì)掛起直到另一個(gè)線程返回該結(jié)果。我們無(wú)需再在另一個(gè)線程中使用回調(diào)函數(shù)來(lái)處理結(jié)果。

前言

Java的多線程機(jī)制允許我們將可以并行的任務(wù)分配給不同的線程同時(shí)完成。但是,如果我們希望在另一個(gè)線程的結(jié)果之上進(jìn)行后續(xù)操作,我們應(yīng)該怎么辦呢?

注:本文的代碼沒(méi)有經(jīng)過(guò)具體實(shí)踐的檢驗(yàn),純屬為了展示。如果有任何問(wèn)題,歡迎指出。

在此之前你需要了解

Thread類(lèi)

Runnable接口

ExecutorServer, Executors生成的線程池

一個(gè)簡(jiǎn)單的場(chǎng)景

假設(shè)我們現(xiàn)在有一個(gè)IO操作需要讀取一個(gè)文件,在讀取完成之后我們希望針對(duì)讀取的字節(jié)進(jìn)行相應(yīng)的處理。因?yàn)镮O操作比較耗時(shí),所以我們可能會(huì)希望在另一個(gè)線程中進(jìn)行IO操作,從而確保主線程的運(yùn)行不會(huì)出現(xiàn)等待。在這里,我們讀取完文件之后會(huì)在其所在線程輸出其字符流對(duì)應(yīng)的字符串。

//主線程類(lèi)
public class MainThread {

    public static void main(String[] args){
        performIO();
    }

    public static void performIO(){
        FileReader fileReader = new FileReader(FILENAME);
        Thread thread = new Thread(fileReader);
        thread.start();
    }
}

文件讀取類(lèi):

public class FileReader implements  Runnable{

    private FileInputStream fileInputStream;
    private String fileName;

    private byte[] content;

    public FileReader(String fileName){
        this.fileName = fileName;
        content = new byte[2048];
    }

    @Override
    public void run() {
        try {
            File file = new File(fileName);
            fileInputStream = new FileInputStream(file);
            int bytesRead = 0;
            while(fileInputStream.available() > 0){
                bytesRead += fileInputStream.read(content, bytesRead, content.length - bytesRead);
            }
            System.out.println(new String(content,0, bytesRead));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
一個(gè)錯(cuò)誤的例子

假設(shè)現(xiàn)在主線程希望針對(duì)文件的信息進(jìn)行操作,那么可能會(huì)出現(xiàn)以下的代碼:

在子線程中添加get方法返回讀取的字符數(shù)組:

public class FileReader implements Runnable{

    private FileInputStream fileInputStream;
    private String fileName;

    private byte[] content;

    //添加get方法返回字符數(shù)組
    public byte[] getContent(){
        return this.content;
    }
    
    public FileReader(String fileName){
        this.fileName = fileName;
        content = new byte[2048];
    }

    @Override
    public void run() {
        try {
            File file = new File(fileName);
            fileInputStream = new FileInputStream(file);
            int bytesRead = 0;
            while(fileInputStream.available() > 0){
                bytesRead += fileInputStream.read(content, bytesRead, content.length - bytesRead);
            }
            System.out.println(new String(content,0, bytesRead));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

主線程方法中添加讀取byte數(shù)組的方法:

public class MainThread {

    public static void main(String[] args){
        performIO();
    }

    public static void performIO(){
        FileReader fileReader = new FileReader(FILENAME);
        Thread thread = new Thread(fileReader);
        thread.start();

        //讀取內(nèi)容
        byte[] content = fileReader.getContent();
        System.out.println(content);
    }
}

這段代碼不能保證正常運(yùn)行,原因在于我們無(wú)法控制線程的調(diào)度。也就是說(shuō),在thread.start()語(yǔ)句后,主線程可能依然占有CPU繼續(xù)執(zhí)行,而此時(shí)獲得的content則是null

你搞定了沒(méi)有啊

主線程可以通過(guò)輪詢(xún)的方式詢(xún)問(wèn)IO線程是不是已經(jīng)完成了操作,如果完成了操作,就讀取結(jié)果。這里我們需要設(shè)置一個(gè)標(biāo)記位來(lái)記錄IO是否完成。

public class FileReader implements Runnable{

    private FileInputStream fileInputStream;
    private String fileName;

    private byte[] content;
    //新建標(biāo)記位,初始為false
    public boolean finish;
    
    public byte[] getContent(){
        return this. content;
    }
    public FileReader(String fileName){
        this.fileName = fileName;
        content = new byte[2048];
    }

    @Override
    public void run() {
        try {
            File file = new File(fileName);
            fileInputStream = new FileInputStream(file);
            int bytesRead = 0;
            while(fileInputStream.available() > 0){
                bytesRead += fileInputStream.read(content, bytesRead, content.length - bytesRead);
            }
            finish = true;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

主線程一直輪詢(xún)IO線程:

public class MainThread {

    public static void main(String[] args){
        performIO();
    }
    
    public static void performIO(){
        FileReader fileReader = new FileReader(FILENAME);
        Thread thread = new Thread(fileReader);
        thread.start();
        while(true){
            if(fileReader.finish){
                System.out.println(new String(fileReader.getContent()));
                break;
            }
        }
    }
}

缺點(diǎn)那是相當(dāng)?shù)拿黠@,不斷的輪詢(xún)會(huì)無(wú)謂的消耗CPU。除此以外,一旦IO異常,則標(biāo)記位永遠(yuǎn)為false,主線程會(huì)陷入死循環(huán)。

搞定了告訴我一聲啊

要解決這個(gè)問(wèn)題,我們就需要在IO線程完成讀取之后,通知主線程該操作已經(jīng)完成,從而主線程繼續(xù)運(yùn)行。這種方法叫做回調(diào)。可以用靜態(tài)方法實(shí)現(xiàn):

public class FileReader implements Runnable{

    private FileInputStream fileInputStream;
    private String fileName;

    private byte[] content;
    
    public FileReader(String fileName){
        this.fileName = fileName;
        content = new byte[2048];
    }

    @Override
    public void run() {
        try {
            File file = new File(fileName);
            fileInputStream = new FileInputStream(file);
            int bytesRead = 0;
            while(fileInputStream.available() > 0){
                bytesRead += fileInputStream.read(content, bytesRead, content.length - bytesRead);
            }
            
            //完成IO后調(diào)用主線程的回調(diào)函數(shù)來(lái)通知主線程進(jìn)行后續(xù)的操作
            MainThread.callback(content);
            
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

主線程方法中定義回調(diào)函數(shù):

public class MainThread {

    public static void main(String[] args){
        performIO();
    }

    //在主線程中用靜態(tài)方法定義回調(diào)函數(shù)
    public static void callback(byte[] content){
        //do something
        System.out.println(content);
    }
    
    public static void performIO(){
        FileReader fileReader = new FileReader(FILENAME);
        Thread thread = new Thread(fileReader);
        thread.start();
    }
}

這種實(shí)現(xiàn)方法的缺點(diǎn)在于MainThread和FileReader類(lèi)之間的耦合太強(qiáng)了。而且萬(wàn)一我們需要讀取多個(gè)文件,我們會(huì)希望對(duì)每一個(gè)FileReader有自己的callback函數(shù)進(jìn)行處理。因此我們可以callback將其聲明為一般函數(shù),并且讓IO線程持有需要回調(diào)的方法所在的實(shí)例:

public class FileReader implements Runnable{

    private FileInputStream fileInputStream;
    private String fileName;

    private byte[] content;

    //持有回調(diào)函數(shù)的實(shí)例
    private MainThread mainThread;
   
    //傳入實(shí)例
    public FileReader(String fileName, MainThreand mainThread){
        this.fileName = fileName;
        content = new byte[2048];
        this.mainThread = mainThread;
    }

    @Override
    public void run() {
        try {
            File file = new File(fileName);
            fileInputStream = new FileInputStream(file);
            int bytesRead = 0;
            while(fileInputStream.available() > 0){
                bytesRead += fileInputStream.read(content, bytesRead, content.length - bytesRead);
            }
            System.out.println(new String(content,0, bytesRead));
            mainThread.callback(content);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

主線程方法中添加讀取byte數(shù)組的方法:

public class MainThread {

    public static void main(String[] args){
        new MainThread().performIO();
    }

    public void callback(byte[] content){
        //do something
    }
    
    //將執(zhí)行IO變?yōu)榉庆o態(tài)方法
    public void performIO(){
        FileReader fileReader = new FileReader(FILENAME);
        Thread thread = new Thread(fileReader);
        thread.start();
    }
}
搞定了告訴我們一聲啊

有時(shí)候可能有多個(gè)事件都在監(jiān)聽(tīng)事件,比如當(dāng)我點(diǎn)擊了Button,我希望后臺(tái)能夠執(zhí)行查詢(xún)操作并將結(jié)果返回給UI。同時(shí),我還希望將用戶(hù)的這個(gè)操作無(wú)論成功與否寫(xiě)入日志線程。因此,我可以寫(xiě)兩個(gè)回調(diào)函數(shù),分別對(duì)應(yīng)于不同的操作。

public interface Callback{
    public void perform(T t);
}

寫(xiě)入日志操作:

public class Log implements Callback{
    public void perform(String s){
        //寫(xiě)入日志
    }
}

IO讀取操作

public class FileReader implements Callback{
    public void perform(String s){
        //進(jìn)行IO操作
    }
}
public class Button{
    private List callables;
    
    public Button(){
        callables = new ArrayList();
    }
    
    public void addCallable(Callable c){
        this.callables.add(c);
    }
    
    public void onClick(){
        for(Callable c : callables){
            c.perform(...);
        }
    }
}
Java7: 行了,別忙活了,朕知道了

Java7提供了非常方便的封裝FutureCallablesExecutors來(lái)實(shí)現(xiàn)之前的回調(diào)工作。

之前我們直接將任務(wù)交給一個(gè)新建的線程來(lái)處理。可是如果每次都新建一個(gè)線程來(lái)處理當(dāng)前的任務(wù),線程的新建和銷(xiāo)毀將會(huì)是一大筆開(kāi)銷(xiāo)。因此Java提供了多種類(lèi)型的線程池來(lái)供我們操作。它將管理線程的創(chuàng)建銷(xiāo)毀和復(fù)用,盡最大可能提高線程的使用效率。

同時(shí)Java7提供的Callable接口將自動(dòng)返回線程運(yùn)行結(jié)束的結(jié)果。如果我們?cè)诹硪粋€(gè)線程中需要使用這個(gè)結(jié)果,則這個(gè)線程會(huì)掛起直到另一個(gè)線程返回該結(jié)果。我們無(wú)需再在另一個(gè)線程中使用回調(diào)函數(shù)來(lái)處理結(jié)果。

假設(shè)現(xiàn)在我們想要找到一個(gè)數(shù)組的最大值。假設(shè)該數(shù)組容量驚人,因此我們希望新開(kāi)兩個(gè)線程分別對(duì)數(shù)組的前半部分和后半部分計(jì)算最大值。然后在主線程中比較兩個(gè)結(jié)果得出結(jié)論:

public class ArrayMaxValue {

    public static void main(String[] args){

        Random r = new Random(20);
        int[] array = new int[500];
        for (int i = 0 ; i f1 = executorService.submit(new MaxValue(array, 0, mid));
        Future f2 = executorService.submit(new MaxValue(array, mid, array.length));

        try {
            //主線程將阻塞自己直到兩個(gè)線程都完成運(yùn)行,并返回結(jié)果
            System.out.println(Math.max(f1.get(), f2.get()));
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
    public class MaxValue implements Callable{
        private final int[] array;
        private final int startIndex;
        private final int endIndex;
        public MaxValue(int[] array, int startIndex, int endIndex){
            this.array = array;
            this.startIndex = startIndex;
            this.endIndex = endIndex;
        }
        @Override
        public Integer call() throws Exception {
            int max = Integer.MIN_VALUE;
            for (int i = startIndex ; i
參考文章
深入理解線程通信


想要了解更多開(kāi)發(fā)技術(shù),面試教程以及互聯(lián)網(wǎng)公司內(nèi)推,歡迎關(guān)注我的微信公眾號(hào)!將會(huì)不定期的發(fā)放福利哦~

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/76369.html

相關(guān)文章

  • 重拾 Java 基礎(chǔ)

    摘要:阿里開(kāi)始招實(shí)習(xí),同學(xué)問(wèn)我要不要去申請(qǐng)阿里的實(shí)習(xí),我說(shuō)不去,個(gè)人對(duì)阿里的印象不好。記得去年阿里給我發(fā)了郵件,我很認(rèn)真地回復(fù),然后他不理我了。 引言 最近好久沒(méi)有遇到技術(shù)瓶頸了,思考得自然少了,每天都是重復(fù)性的工作。 阿里開(kāi)始招實(shí)習(xí),同學(xué)問(wèn)我要不要去申請(qǐng)阿里的實(shí)習(xí),我說(shuō)不去,個(gè)人對(duì)阿里的印象不好。 記得去年阿里給我發(fā)了郵件,我很認(rèn)真地回復(fù),然后他不理我了。(最起碼的尊重都沒(méi)有,就算我菜你起...

    ideaa 評(píng)論0 收藏0
  • 重拾Java Network Programming(二)InetAddress

    摘要:前言今天,我將梳理在網(wǎng)絡(luò)編程中很重要的一個(gè)類(lèi)以及其相關(guān)的類(lèi)。這類(lèi)主機(jī)通常不需要外部互聯(lián)網(wǎng)服務(wù),僅有主機(jī)間相互通訊的需求。可以通過(guò)該接口獲取所有本地地址,并根據(jù)這些地址創(chuàng)建。在這里我們使用阻塞隊(duì)列實(shí)現(xiàn)主線程和打印線程之間的通信。 前言 今天,我將梳理在Java網(wǎng)絡(luò)編程中很重要的一個(gè)類(lèi)InetAddress以及其相關(guān)的類(lèi)NetworkInterface。在這篇文章中將會(huì)涉及: InetA...

    daryl 評(píng)論0 收藏0
  • 重拾Java Network Programming(四)URLConnection & C

    摘要:從而一方面減少了響應(yīng)時(shí)間,另一方面減少了服務(wù)器的壓力。表明響應(yīng)只能被單個(gè)用戶(hù)緩存,不能作為共享緩存即代理服務(wù)器不能緩存它。這種情況稱(chēng)為服務(wù)器再驗(yàn)證。否則會(huì)返回響應(yīng)。 前言 本文將根據(jù)最近所學(xué)的Java網(wǎng)絡(luò)編程實(shí)現(xiàn)一個(gè)簡(jiǎn)單的基于URL的緩存。本文將涉及如下內(nèi)容: HTTP協(xié)議 HTTP協(xié)議中與緩存相關(guān)的內(nèi)容 URLConnection 和 HTTPURLConnection Respo...

    Guakin_Huang 評(píng)論0 收藏0
  • 重拾Java Network Programming(四)URLConnection & C

    摘要:從而一方面減少了響應(yīng)時(shí)間,另一方面減少了服務(wù)器的壓力。表明響應(yīng)只能被單個(gè)用戶(hù)緩存,不能作為共享緩存即代理服務(wù)器不能緩存它。這種情況稱(chēng)為服務(wù)器再驗(yàn)證。否則會(huì)返回響應(yīng)。 前言 本文將根據(jù)最近所學(xué)的Java網(wǎng)絡(luò)編程實(shí)現(xiàn)一個(gè)簡(jiǎn)單的基于URL的緩存。本文將涉及如下內(nèi)容: HTTP協(xié)議 HTTP協(xié)議中與緩存相關(guān)的內(nèi)容 URLConnection 和 HTTPURLConnection Respo...

    魏明 評(píng)論0 收藏0
  • 后臺(tái) - 收藏集 - 掘金

    摘要:探究系統(tǒng)登錄驗(yàn)證碼的實(shí)現(xiàn)后端掘金驗(yàn)證碼生成類(lèi)手把手教程后端博客系統(tǒng)第一章掘金轉(zhuǎn)眼間時(shí)間就從月份到現(xiàn)在的十一月份了。提供了與標(biāo)準(zhǔn)不同的工作方式我的后端書(shū)架后端掘金我的后端書(shū)架月前本書(shū)架主要針對(duì)后端開(kāi)發(fā)與架構(gòu)。 Spring Boot干貨系列總綱 | 掘金技術(shù)征文 - 掘金原本地址:Spring Boot干貨系列總綱博客地址:http://tengj.top/ 前言 博主16年認(rèn)識(shí)Spin...

    CrazyCodes 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<