java – 了解线程异步
所以我有一个程序,我需要向URL发送很多(如10,000)GET请求,我需要它尽可能快.当我第一次创建程序时,我只是将连接放入for循环中,但它确实很慢,因为它必须等待每个连接完成才能继续.我想让它更快,所以我尝试使用线程,它使它更快,但我仍然不满意.
我猜测正确的方法是使用异步连接并连接到所有URL.这是正确的方法吗? 此外,我一直在努力了解线程以及它们是如何工作的,但我似乎无法理解它.我所使用的计算机配有Intel Core i7-3610QM四核处理器.根据英特尔网站上有关该处理器的规格,它有8个线程.这是否意味着我可以在Java应用程序中创建8个线程,它们将同时运行?超过8,没有速度增加? “性能”选项卡下任务管理器中“线程”旁边的数字究竟代表什么?目前,我的任务管理器显示“线程”超过1,000.为什么这是这个数字,如果那是我的所有处理器支持,它怎么能超过8呢? 另外,我尝试用Java中的线程做一个小测试,但输出对我来说没有意义. import java.text.SimpleDateFormat; import java.util.Date; public class Test { private static int numThreads = 3; private static int numLoops = 100000; private static SimpleDateFormat dateFormat = new SimpleDateFormat("[hh:mm:ss] "); public static void main(String[] args) throws Exception { for (int i=1; i<=numThreads; i++) { final int threadNum = i; new Thread(new Runnable() { public void run() { System.out.println(dateFormat.format(new Date()) + "Start of thread: " + threadNum); for (int i=0; i<numLoops; i++) for (int j=0; j<numLoops; j++); System.out.println(dateFormat.format(new Date()) + "End of thread: " + threadNum); } }).start(); Thread.sleep(2000); } } } 这会产生如下输出: [09:48:51] Start of thread: 1 [09:48:53] Start of thread: 2 [09:48:55] Start of thread: 3 [09:48:55] End of thread: 3 [09:48:56] End of thread: 1 [09:48:58] End of thread: 2 为什么第三个线程立即开始和结束,而第一个和第二个线程各占5秒?如果我添加更多3个线程,对于2以上的所有线程都会发生同样的事情. 对不起,如果这是一个长篇阅读,我有很多问题. 解决方法
您的处理器有8个内核,而不是线程.事实上,这意味着在任何给定时刻只能运行8件事.但这并不意味着您仅限于8个线程.
当一个线程同步打开一个到URL的连接时,它会在等待远程服务器返回它时经常休眠.当该线程正在休眠时,其他线程可以正常工作.如果你有500个线程并且所有500个线程都处于休眠状态,那么你就不会使用CPU的任何内核. 另一方面,如果你有500个线程并且所有500个线程想要做某事,那么它们不能同时运行.为了处理这种情况,有一个特殊的工具.处理器(或者更可能是操作系统或两者的某种组合)具有调度器,该调度器确定在任何给定时间哪些线程在处理器上主动运行.有许多不同的规则,有时是随机活动,可以控制这些调度程序的工作方式.这可以解释为什么在上面的示例中,线程3似乎总是先完成.也许调度程序更喜欢线程3,因为它是主线程调度的最新线程,有时候无法预测行为. 现在回答你关于表现的问题.如果打开连接从不涉及睡眠,那么无论是同步处理还是异步处理都无关紧要,您将无法获得超过8个线程的任何性能提升.实际上,打开连接所花费的大部分时间都花在睡觉上.异步和同步之间的区别在于如何处理睡眠时间.从理论上讲,你应该能够在两者之间获得几乎相同的表现. 使用多线程模型,您只需创建比核心更多的线程.当线程进入休眠状态时,他们让其他线程工作.这有时可以更容易处理,因为您不必在线程之间编写任何调度或交互. 使用异步模型,您只能为每个核心创建一个线程.如果该线程需要休眠,那么它不会休眠,但实际上必须有代码来处理切换到下一个连接.例如,假设打开连接(A,B,C)有三个步骤: while (!connectionsList.isEmpty()) { for(Connection connection : connectionsList) { if connection.getState() == READY_FOR_A { connection.stepA(); //this method should return immediately and the connection //should go into the waiting state for some time before going //into the READY_FOR_B state } if connection.getState() == READY_FOR_B { connection.stepB(); //same immediate return behavior as above } if connection.getState() == READY_FOR_C { connection.stepC(); //same immediate return behavior as above } if connection.getState() == WAITING { //Do nothing,skip over } if connection.getState() == FINISHED { connectionsList.remove(connection); } } } 请注意,线程在任何时候都不会休眠,因此没有任何关键点拥有比核心更多的线程.最终,无论是采用同步方法还是异步方法都是个人偏好的问题.只有在绝对极端情况下,两者之间才会存在性能差异,您需要花费很长时间进行分析才能达到应用程序瓶颈的程度. 听起来你创造了很多线程而没有获得任何性能提升.这可能有很多原因. >你建立一个连接可能实际上没有睡觉,在这种情况下我不希望看到超过8个线程的性能提升.我不认为这是可能的. 如果我是你,我会使用像JVisualVM这样的工具在运行一些少量线程时分析你的应用程序(20). JVisualVM有一个很好的彩色线程图,它将显示线程何时运行,阻塞或休眠.这将帮助您了解线程/核心关系,因为您应该看到正在运行的线程数小于您拥有的核心数.此外,如果您看到许多被阻塞的线程,那么这可以帮助您引导您的瓶颈(如果您看到许多被阻塞的线程使用JVisualVM在该时间点创建线程转储并查看线程被阻止的内容). (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |