java 9多线程HTTP客户端不';我不能正常工作

java 9多线程HTTP客户端不';我不能正常工作,java,multithreading,http2,Java,Multithreading,Http2,我试图将下面的顺序代码转换为多线程代码,但结果听起来不合理 package com.net; import jdk.incubator.http.HttpClient; import jdk.incubator.http.HttpRequest; import jdk.incubator.http.HttpResponse; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import java.io.IOException;

我试图将下面的顺序代码转换为多线程代码,但结果听起来不合理

package com.net;

import jdk.incubator.http.HttpClient;
import jdk.incubator.http.HttpRequest;
import jdk.incubator.http.HttpResponse;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;

public class Req {

    HttpClient client = HttpClient.newHttpClient();

    private String getResource(String someUrl) {
        String body = "";
        try {
            URI url = new URI(someUrl);
            HttpRequest request = HttpRequest.newBuilder()
                    .uri(url).GET().build();
            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandler.asString());
            body = response.body();
        } catch (URISyntaxException e) {
            System.out.println("URL " + someUrl + "is not valid");
        } catch (IOException | InterruptedException e) {
            System.out.println(e.getMessage());
        }
        return body;
    }


    public static void main(String[] args){

        String[] topIranianSites = {
                "https://www.aparat.com/",
                "http://www.varzesh3.com/",
                "http://namnak.com/",
                "http://www.telewebion.com/",
                "https://divar.ir/",
                "https://www.ninisite.com/",
                "https://www.blogfa.com/",
                "http://www.namasha.com/",
                "http://www.yjc.ir/"
        };

        Req singleThreadReq = new Req();
        float totalElapsedTime = 0F;

        for (String site : topIranianSites){
            long fetchStartTime = System.currentTimeMillis();
            String html = singleThreadReq.getResource(site);
            float elapsed = (float) (System.currentTimeMillis() - fetchStartTime) / 1000;

            Document doc = Jsoup.parse(html);
            System.out.println("It took " + elapsed + " seconds to fetch " + site + " with title " + doc.title());
            totalElapsedTime += elapsed;
        }

        System.out.println("Total Elapsed Time: " + totalElapsedTime + "\nTotal Number of sites: " + topIranianSites.length);

    }
}
根据顺序输出,我猜正确的多线程代码应该需要大约2.8秒才能获取所有9个站点。但是我的多线程代码实现需要更多的时间

package com.net;

import jdk.incubator.http.HttpClient;
import jdk.incubator.http.HttpRequest;
import jdk.incubator.http.HttpResponse;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;

public class ReqThreaded implements Runnable {

    class Site {
        String url;
        String title;
        float fetchTime;
    }

    private HttpClient client = HttpClient.newHttpClient();

    private Thread[] threadPool;
    private String[] rawSites;
    private Site[] sitesArr;
    private int sitesDone = 0;
    long startTime = System.currentTimeMillis();
    float totalElapsed = 0F;

    public ReqThreaded(String[] sites) {
        threadPool = new Thread[sites.length];
        sitesArr = new Site[sites.length];
        rawSites = sites;

        for (int i = 0; i < sites.length; i++) {
            startThread(i);
        }

        while (sitesDone < sites.length) {
            try {
                Thread.sleep(1000);
                totalElapsed = (float) (System.currentTimeMillis() - startTime) / 1000;
                System.out.print("\rElapsed time: " + totalElapsed + "Sites Done: " + sitesDone);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("\n\nStatistics:\n\n");

        for (Site someSite : sitesArr) {
            System.out.println("URL " + someSite.url + "\nTitle: " + someSite.title + "\nFetch Time: " + someSite.fetchTime + "\n\n");
        }

    }

    private void startThread(int i) {
        if (threadPool[i] == null) {
            threadPool[i] = new Thread(this);
            threadPool[i].start();
        }
    }


    private String getResource(String someUrl) {
        String body = "";
        try {
            URI url = new URI(someUrl);
            HttpRequest request = HttpRequest.newBuilder()
                    .uri(url).GET().build();
            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandler.asString());
            body = response.body();
        } catch (URISyntaxException e) {
            System.out.println("URL " + someUrl + "is not valid");
        } catch (IOException | InterruptedException e) {
            System.out.println(e.getMessage());
        }
        return body;
    }


    @Override
    public void run() {
        Thread thisThread = Thread.currentThread();
        int sitesIndex = 0;

        for (int j = 0; j < threadPool.length; j++) {
            if (thisThread == threadPool[j]) {
                sitesIndex = j;
            }
        }
        long fetchStartTime = System.currentTimeMillis();
        String html = getResource(rawSites[sitesIndex]);
        float elapsed = (float) (System.currentTimeMillis() - fetchStartTime) / 1000;
        sitesDone++;
        Document doc = Jsoup.parse(html);
        sitesArr[sitesIndex] = new Site();
        sitesArr[sitesIndex].url = rawSites[sitesIndex];
        sitesArr[sitesIndex].title = doc.title();
        sitesArr[sitesIndex].fetchTime = elapsed;
    }

    public static void main(String[] args) {

        String[] topIranianSites = {
                "https://www.aparat.com/",
                "http://www.varzesh3.com/",
                "http://namnak.com/",
                "http://www.telewebion.com/",
                "https://divar.ir/",
                "https://www.ninisite.com/",
                "https://www.blogfa.com/",
                "http://www.namasha.com/",
                "http://www.yjc.ir/"
        };

        new ReqThreaded(topIranianSites);

    }
}

我修复了类
ReqThreaded
中的一些明显错误,例如不正确的同步,将方法
run()
移动到类
Site
,并使结果打印与单线程变体中的结果打印相同。未使用线程池,为每个请求创建一个单独的线程

结果如下:

Received fatal alert: handshake_failure
It took 0.263 seconds to fetch https://www.blogfa.com/ with title 
General SSLEngine problem
It took 0.491 seconds to fetch https://divar.ir/ with title 
It took 1.02 seconds to fetch http://www.yjc.ir/ with title 
It took 1.056 seconds to fetch http://www.telewebion.com/ with title تلوبیون | مرجع پخش زنده و دانلود فیلم ، سریال و سایر برنامه های تلویزیون
It took 1.262 seconds to fetch https://www.ninisite.com/ with title نی نی سایت | راهنمای بارداری و بچه داری
It took 1.411 seconds to fetch http://namnak.com/ with title نمناک
It took 1.608 seconds to fetch http://www.varzesh3.com/ with title ورزش سه :: صفحه اصلی
It took 2.221 seconds to fetch http://www.namasha.com/ with title نماشا - سرویس رایگان اشتراک ویدیو
It took 2.247 seconds to fetch https://www.aparat.com/ with title آپارات - سرویس اشتراک ویدیو
Elapsed time: 2.253
Sites Done: 9
Process finished with exit code 0
import jdk.incubator.http.HttpClient;
import jdk.incubator.http.HttpRequest;
import jdk.incubator.http.HttpResponse;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.concurrent.CountDownLatch;

public class ReqThreaded {

class Site implements Runnable {
    final String url;
    String title;
    float fetchTime;

    Site(String url) {
        this.url = url;
    }

    @Override
    public void run() {
        long fetchStartTime = System.currentTimeMillis();
        String html = getResource(url);
        float elapsed = (float) (System.currentTimeMillis() - fetchStartTime) / 1000;
        Document doc = Jsoup.parse(html);
        title = doc.title();
        fetchTime = elapsed;
        System.out.println("It took " + fetchTime + " seconds to fetch " + url + " with title " + title);
        sitesDone.countDown();
    }
}

private HttpClient client = HttpClient.newHttpClient();

private CountDownLatch sitesDone;

public ReqThreaded(String[] sites) throws InterruptedException {
    int siteNumber = sites.length;
    sitesDone = new CountDownLatch(siteNumber);
    long startTime = System.currentTimeMillis();

    for (int i = 0; i < siteNumber; i++) {
        Runnable site = new Site(sites[i]);
        Thread thread = new Thread(site);
        thread.start();
    }

    sitesDone.await();
    float totalElapsed = (float) (System.currentTimeMillis() - startTime) / 1000;
    System.out.print("\rElapsed time: " + totalElapsed + "\nSites Done: " + siteNumber);
}

private String getResource(String someUrl) {
    String body = "";
    try {
        URI url = new URI(someUrl);
        HttpRequest request = HttpRequest.newBuilder()
                .uri(url).GET().build();
        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandler.asString());
        body = response.body();
    } catch (URISyntaxException e) {
        System.out.println("URL " + someUrl + "is not valid");
    } catch (IOException | InterruptedException e) {
        System.out.println(e.getMessage());
    }
    return body;
}

public static void main(String[] args) throws InterruptedException {

    String[] topIranianSites = {
            "https://www.aparat.com/",
            "http://www.varzesh3.com/",
            "http://namnak.com/",
            "http://www.telewebion.com/",
            "https://divar.ir/",
            "https://www.ninisite.com/",
            "https://www.blogfa.com/",
            "http://www.namasha.com/",
            "http://www.yjc.ir/"
    };

    new ReqThreaded(topIranianSites);

}
}
也就是说,总时间仅略大于从站点获取结果的最长时间

多线程工作

修改代码如下:

Received fatal alert: handshake_failure
It took 0.263 seconds to fetch https://www.blogfa.com/ with title 
General SSLEngine problem
It took 0.491 seconds to fetch https://divar.ir/ with title 
It took 1.02 seconds to fetch http://www.yjc.ir/ with title 
It took 1.056 seconds to fetch http://www.telewebion.com/ with title تلوبیون | مرجع پخش زنده و دانلود فیلم ، سریال و سایر برنامه های تلویزیون
It took 1.262 seconds to fetch https://www.ninisite.com/ with title نی نی سایت | راهنمای بارداری و بچه داری
It took 1.411 seconds to fetch http://namnak.com/ with title نمناک
It took 1.608 seconds to fetch http://www.varzesh3.com/ with title ورزش سه :: صفحه اصلی
It took 2.221 seconds to fetch http://www.namasha.com/ with title نماشا - سرویس رایگان اشتراک ویدیو
It took 2.247 seconds to fetch https://www.aparat.com/ with title آپارات - سرویس اشتراک ویدیو
Elapsed time: 2.253
Sites Done: 9
Process finished with exit code 0
import jdk.incubator.http.HttpClient;
import jdk.incubator.http.HttpRequest;
import jdk.incubator.http.HttpResponse;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.concurrent.CountDownLatch;

public class ReqThreaded {

class Site implements Runnable {
    final String url;
    String title;
    float fetchTime;

    Site(String url) {
        this.url = url;
    }

    @Override
    public void run() {
        long fetchStartTime = System.currentTimeMillis();
        String html = getResource(url);
        float elapsed = (float) (System.currentTimeMillis() - fetchStartTime) / 1000;
        Document doc = Jsoup.parse(html);
        title = doc.title();
        fetchTime = elapsed;
        System.out.println("It took " + fetchTime + " seconds to fetch " + url + " with title " + title);
        sitesDone.countDown();
    }
}

private HttpClient client = HttpClient.newHttpClient();

private CountDownLatch sitesDone;

public ReqThreaded(String[] sites) throws InterruptedException {
    int siteNumber = sites.length;
    sitesDone = new CountDownLatch(siteNumber);
    long startTime = System.currentTimeMillis();

    for (int i = 0; i < siteNumber; i++) {
        Runnable site = new Site(sites[i]);
        Thread thread = new Thread(site);
        thread.start();
    }

    sitesDone.await();
    float totalElapsed = (float) (System.currentTimeMillis() - startTime) / 1000;
    System.out.print("\rElapsed time: " + totalElapsed + "\nSites Done: " + siteNumber);
}

private String getResource(String someUrl) {
    String body = "";
    try {
        URI url = new URI(someUrl);
        HttpRequest request = HttpRequest.newBuilder()
                .uri(url).GET().build();
        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandler.asString());
        body = response.body();
    } catch (URISyntaxException e) {
        System.out.println("URL " + someUrl + "is not valid");
    } catch (IOException | InterruptedException e) {
        System.out.println(e.getMessage());
    }
    return body;
}

public static void main(String[] args) throws InterruptedException {

    String[] topIranianSites = {
            "https://www.aparat.com/",
            "http://www.varzesh3.com/",
            "http://namnak.com/",
            "http://www.telewebion.com/",
            "https://divar.ir/",
            "https://www.ninisite.com/",
            "https://www.blogfa.com/",
            "http://www.namasha.com/",
            "http://www.yjc.ir/"
    };

    new ReqThreaded(topIranianSites);

}
}
导入jdk.incubator.http.HttpClient;
导入jdk.incubator.http.HttpRequest;
导入jdk.incubator.http.HttpResponse;
导入org.jsoup.jsoup;
导入org.jsoup.nodes.Document;
导入java.io.IOException;
导入java.net.URI;
导入java.net.URISyntaxException;
导入java.util.concurrent.CountDownLatch;
公开课{
类站点实现可运行{
最终字符串url;
字符串标题;
浮动时间;
站点(字符串url){
this.url=url;
}
@凌驾
公开募捐{
long fetchStartTime=System.currentTimeMillis();
字符串html=getResource(url);
浮点已用=(浮点)(System.currentTimeMillis()-fetchStartTime)/1000;
Document doc=Jsoup.parse(html);
title=doc.title();
fetchTime=经过的时间;
System.out.println(“用“+fetchTime+”秒来获取“+url+”和标题“+title”);
sitesDone.countDown();
}
}
私有HttpClient=HttpClient.newHttpClient();
私人倒计时闩锁站点;
public ReqThreaded(字符串[]站点)引发InterruptedException{
int siteNumber=sites.length;
SitesOne=新的倒计时闩锁(siteNumber);
long startTime=System.currentTimeMillis();
对于(int i=0;i
即:

  • 切勿在不同线程上同时运行同一对象的同一方法

  • 不要通过普通变量在线程之间交换信息。仅使用专用设施。在这里,我使用了
    CountDownLatch
    ,来表示每个线程的结束。如果我想向主线程返回一些信息,我会使用
    BlockingQueue


首先,第二个
线程睡眠(1000)
将不必要地增加持续时间。您应该改用
ExecutorService
。问题中90%的代码是不必要的,可能是错误的。@devpuh我认为线程比进程轻得多,无法处理阻塞操作,那么正确的方法是什么呢?使用协同路由的async await是python中处理此类作业的最佳方法。在java中最好的方法是什么?@kayman你能给我一个正确的答案吗?@m.d确实,java线程比进程轻。但每个线程的堆栈都需要内存,通常为0.1-1兆字节。因此,正确的方法是在您有能力花费内存的情况下使用线程,然后切换到任务和线程池。感谢您改进代码,但多线程代码在哪里?我是java的新手,但我也这么认为。我认为我的多线程代码实现中的主要问题是当多个线程试图访问对象的同一方法时。它有某种阻塞效应,我想你能详细说明一下吗?当我在第五个站点之后运行你的代码时,会从1.8秒跳到3秒以上,并在7秒以上结束。为什么?你不能说“在第五个站点之后”,因为所有站点都是并行请求的:请求同时开始,但在网站回复时结束。事实上,有些网站回复速度很快,有些则会延迟。将总运行时间与单个站点的最长时间进行比较。如果他们很接近,那没关系。谢谢亚历克斯。在您的提示和指导下,我编写了另一个多线程代码的实现,它在我的测试中运行得非常快。跳闸时间范围在0.2-1.3秒之间,但在您的实施中,跳闸时间在0.95-11.57秒之间。既然您是并发方面的专家,请您看一下代码,并告诉我为什么trip代码会有如此大的差异?我搞不懂。我测试的网站是世界上最受欢迎的网站