亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

重拾JAVA線程之獲取另一個線程的返回

liuchengxu / 1660人閱讀

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

前言

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

注:本文的代碼沒有經(jīng)過具體實踐的檢驗,純屬為了展示。如果有任何問題,歡迎指出。

在此之前你需要了解

Thread類

Runnable接口

ExecutorServer, Executors生成的線程池

一個簡單的場景

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

//主線程類
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();
    }
}

文件讀取類:

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();
        }
    }
}
一個錯誤的例子

假設(shè)現(xiàn)在主線程希望針對文件的信息進行操作,那么可能會出現(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);
    }
}

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

你搞定了沒有啊

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

public class FileReader implements Runnable{

    private FileInputStream fileInputStream;
    private String fileName;

    private byte[] content;
    //新建標記位,初始為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();
        }
    }
}

主線程一直輪詢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;
            }
        }
    }
}

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

搞定了告訴我一聲啊

要解決這個問題,我們就需要在IO線程完成讀取之后,通知主線程該操作已經(jīng)完成,從而主線程繼續(xù)運行。這種方法叫做回調(diào)??梢杂渺o態(tài)方法實現(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ù)來通知主線程進行后續(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();
    }
}

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

public class FileReader implements Runnable{

    private FileInputStream fileInputStream;
    private String fileName;

    private byte[] content;

    //持有回調(diào)函數(shù)的實例
    private MainThread mainThread;
   
    //傳入實例
    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();
    }
}
搞定了告訴我們一聲啊

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

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

寫入日志操作:

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

IO讀取操作

public class FileReader implements Callback{
    public void perform(String s){
        //進行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提供了非常方便的封裝Future,CallablesExecutors來實現(xiàn)之前的回調(diào)工作。

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

同時Java7提供的Callable接口將自動返回線程運行結(jié)束的結(jié)果。如果我們在另一個線程中需要使用這個結(jié)果,則這個線程會掛起直到另一個線程返回該結(jié)果。我們無需再在另一個線程中使用回調(diào)函數(shù)來處理結(jié)果。

假設(shè)現(xiàn)在我們想要找到一個數(shù)組的最大值。假設(shè)該數(shù)組容量驚人,因此我們希望新開兩個線程分別對數(shù)組的前半部分和后半部分計算最大值。然后在主線程中比較兩個結(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 {
            //主線程將阻塞自己直到兩個線程都完成運行,并返回結(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
參考文章
深入理解線程通信


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

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

轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/76369.html

相關(guān)文章

  • 重拾 Java 基礎(chǔ)

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

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

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

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

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

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

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

    魏明 評論0 收藏0
  • 后臺 - 收藏集 - 掘金

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

    CrazyCodes 評論0 收藏0

發(fā)表評論

0條評論

閱讀需要支付1元查看
<