JAVA基础知识之网络编程——-网络基础(Java的http get和post请求,多线程下载)

时间:2021-06-20 16:17:25

本文主要介绍java.net下为网络编程提供的一些基础包,InetAddress代表一个IP协议对象,可以用来获取IP地址,Host name之类的信息。URL和URLConnect可以用来访问web资源,URLDecode和URLEncode用来转换字符串。 本文会写两个例子来演示java网络编程的一些基本用法。

第一个例子,用java实现http get 和 post请求

本例涉及如下知识点,

  • HTT协议规范
  • 使用URL对象建立HTTP协议的网络连接URLConnection对象conn,本地和网络通信就靠这个对象的getInputStreamgetOutputStream方法
  • conn返回的inputStream和outputStream都是字节流(byte stream)
  • JAVA IO中的BufferedReader,属于reader族,是可以缓存的字符流(characters stream),是更直观的读取方式。
  • JAVA IO中的InputStreamReader,也属于reader族,是字节流这字符流的桥梁。 本例中使用它把网络读取的inputStream转换成了BufferedReader。
  • 注意在转换字节流到字符流的过程中,需要参考数据源进行编码转换,本例使用utf-8编码

具体实现代码如下, 本例参考了博客 http://www.cnblogs.com/zhuawang/archive/2012/12/08/2809380.html (谢谢, 已对编码部分优化)

 package network;

 import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Map; public class HttpRestTest { public static String sendGet(String url, String param) {
String result = "";
BufferedReader in = null;
try {
String urlNameString = url + "?" + param;
URL resultUrl = new URL(urlNameString);
URLConnection connection = resultUrl.openConnection();
//设置通用属性
connection.setRequestProperty("accept", "*/");
connection.setRequestProperty("connection", "Keep-Alive");
connection.connect();
//获取响应头
Map<String, List<String>> map = connection.getHeaderFields(); for(String key:map.keySet()) {
System.out.println(key+"--->"+map.get(key));
}
Charset cs = Charset.forName("utf-8");
in = new BufferedReader(new InputStreamReader(connection.getInputStream(),cs));
String line;
while((line = in.readLine()) != null) {
result += line;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (in != null) {
in.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
return result;
} public static String sendPost(String url, String param) {
PrintWriter out = null;
BufferedReader in = null;
String result = "";
try {
String urlNameString = url + "?" + param;
URL resultUrl = new URL(urlNameString);
URLConnection conn = resultUrl.openConnection();
//设置通用属性
conn.setRequestProperty("accept", "*/");
conn.setRequestProperty("connection", "Keep-Alive");
//发送post请求必须设置下面两行
conn.setDoOutput(true);
conn.setDoInput(true);
out = new PrintWriter(conn.getOutputStream());
out.print(param);
out.flush();
Charset cs = Charset.forName("utf-8");
in = new BufferedReader(new InputStreamReader(conn.getInputStream(), cs));
String line;
while((line = in.readLine()) != null) {
result += line;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (out != null) {
out.close();
}
if (in != null) {
in.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
return result;
} public static void main(String[] args) {
String get = HttpRestTest.sendGet("http://fysola.cnblogs.com/", "");
System.out.println(get);
System.out.println("================================================");
String post = HttpRestTest.sendGet("http://fysola.cnblogs.com/", "");
System.out.println(post); }
}

执行结果如下,

 null--->[HTTP/1.1 200 OK]
Vary--->[Accept-Encoding]
Date--->[Sat, 19 Nov 2016 04:50:32 GMT]
Content-Length--->[14744]
Expires--->[Sat, 19 Nov 2016 04:50:41 GMT]
X-UA-Compatible--->[IE=10]
Last-Modified--->[Sat, 19 Nov 2016 04:50:31 GMT]
Connection--->[keep-alive]
Content-Type--->[text/html; charset=utf-8]
Cache-Control--->[private, max-age=10]
<!DOCTYPE html><html lang="zh-cn"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1" /><title>fysola - 博客园</title><link type="text/css" rel="stylesheet" href="/bundles/blog-common.css?v=Rdf1BBttS5_qVaET1myrajVTd62BSCCoJA9fZxGv1ZM1"/><link id="MainCss" type="text/css" rel="stylesheet" href="/skins/LessIsMore/bundle-LessIsMore.css?v=0F7NKnmmVYJDCp-KQ2sA7Dn1GREfDmWWyNjiJP9YrzE1"/><link id="mobile-style" media="only screen and (max-width: 768px)" type="text/css" rel="stylesheet" href="/skins/LessIsMore/bundle-LessIsMore-mobile.css?v=-559DaKqzQsHh-ZmNj3u85wntv3Lbkjpzbkkq1zkN_E1"/><link title="RSS" type="application/rss+xml" rel="alternate" href="http://www.cnblogs.com/fysola/rss"/><link title="RSD" type="application/rsd+xml" rel="EditURI" href="http://www.cnblogs.com/fysola/rsd.xml"/><link type="application/wlwmanifest+xml" rel="wlwmanifest" href="http://www.cnblogs.com/fysola/wlwmanifest.xml"/><script src="//common.cnblogs.com/script/jquery.js" type="text/javascript"></script> <script type="text/javascript">var currentBlogApp = 'fysola', cb_enable_mathjax=false;var isLogined=false;</script><script src="/bundles/blog-common.js?v=hH1lCMV8WaIu271Nx7jPuv36TENW9-RsSxziLxUpjtc1" type="text/javascript"></script></head><body><a name="top"></a><div id="home"><div id="header"> <div id="blogTitle"> <!--done--><div class="title"><a id="Header1_HeaderTitle" class="headermaintitle" href="http://www.cnblogs.com/fysola/">云中之歌</a></div><div class="subtitle">Practice like your performance</div> </div><!--end: blogTitle 博客的标题和副标题 --> <div id="navigator"> <ul id="navList"><li id="nav_sitehome"></li><li id="nav_myhome"><a id="blog_nav_myhome" class="menu" href="http://www.cnblogs.com/fysola/">首页</a></li><li id="nav_newpost"></li><li id="nav_contact"></li><li id="nav_rss"><a id="blog_nav_rss" class="menu" href="http://www.cnblogs.com/fysola/rss">订阅</a><!--<a id="blog_nav_rss_image" class="aHeaderXML" href="http://www.cnblogs.com/fysola/rss"><img src="//www.cnblogs.com/images/xml.gif" alt="订阅" /></a>--></li><li id="nav_admin"><a id="blog_nav_admin" class="menu" rel="nofollow" href="https://i.cnblogs.com/">管理</a></li></ul> <div class="blogStats"> </div><!--end: blogStats --> </div><!--end: navigator 博客导航栏 --></div><!--end: header 头部 --><div id="main"> <div id="mainContent"> <div class="forFlow"> <!--done--><div class="day"> <div class="dayTitle"> <a id="homepage1_HomePageDays_DaysList_ctl00_ImageLink" href="http://www.cnblogs.com/fysola/archive/2016/11/19.html">2016年11月19日</a> </div> <div class="postTitle"> <a id="homepage1_HomePageDays_DaysList_ctl00_DayList_TitleUrl_0" class="postTitle2" href="http://www.cnblogs.com/fysola/p/6080052.html">JAVA基础知识之网络编程——-网络基础(多线程下载,get,post)</a> </div> <div class="postCon"><div class="c_b_p_desc">摘要: 本文主要介绍java.net下为网络编程提供的一些基础包,InetAddress代表一个IP协议对象,可以用来获取IP地址,Host name之类的信息。URL和URLConnect可以用来访问web资源,URLDecode和URLEncode用来转换字符串。 本文会写两个例子来演示java网络编程<a href="http://www.cnblogs.com/fysola/p/6080052.html" class="c_b_p_desc_readmore">阅读全文</a></div></div> <div class="clear"></div> <div class="postDesc">posted @ 2016-11-19 10:42 fysola 阅读(1) 评论(0) <a href ="https://i.cnblogs.com/EditPosts.aspx?postid=6080052" rel="nofollow">编辑</a></div> <div class="clear"></div> </div><div class="day"> <div class="dayTitle"> <a id="homepage1_HomePageDays_DaysList_ctl01_ImageLink" href="http://www.cnblogs.com/fysola/archive/2016/11/18.html">2016年11月18日</a> </div> <div class="postTitle"> <a id="homepage1_HomePageDays_DaysList_ctl01_DayList_TitleUrl_0" class="postTitle2" href="http://www.cnblogs.com/fysola/p/6076118.html">JAVA基础知识之多线程——线程池</a> </div> <div class="postCon"><div class="c_b_p_desc">摘要: 线程池概念 操作系统或者JVM创建一个线程以及销毁一个线程都需要消耗CPU资源,如果创建或者销毁线程的消耗源远远小于执行一个线程的消耗,则可以忽略不计,但是基本相等或者大于执行线程的消耗,而且需要创建大批量这种线程的话,CPU将资源将会大量消耗在创建线程和销毁线程上,这是不能接受的,因此我们需要一个<a href="http://www.cnblogs.com/fysola/p/6076118.html" class="c_b_p_desc_readmore">阅读全文</a></div></div> <div class="clear"></div> <div class="postDesc">posted @ 2016-11-18 00:33 fysola 阅读(2) 评论(0) <a href ="https://i.cnblogs.com/EditPosts.aspx?postid=6076118" rel="nofollow">编辑</a></div> <div class="clear"></div> </div><div class="day"> <div class="dayTitle"> <a id="homepage1_HomePageDays_DaysList_ctl02_ImageLink" href="http://www.cnblogs.com/fysola/archive/2016/11/17.html">2016年11月17日</a> </div> <div class="postTitle"> <a id="homepage1_HomePageDays_DaysList_ctl02_DayList_TitleUrl_0" class="postTitle2" href="http://www.cnblogs.com/fysola/p/6074862.html">JAVA基础知识之多线程——线程组和未处理异常</a> </div> <div class="postCon"><div class="c_b_p_desc">摘要: 线程组 Java中的ThreadGroup类表示线程组,在创建新线程时,可以通过构造函数Thread(group...)来指定线程组。 线程组具有以下特征 如果没有显式指定线程组,则新线程属于默认线程组,默认情况下,与创建线程所在的组相同 一旦确定了线程所在线程组之后,不允许更改线程组,直到线程死亡<a href="http://www.cnblogs.com/fysola/p/6074862.html" class="c_b_p_desc_readmore">阅读全文</a></div></div> <div class="clear"></div> <div class="postDesc">posted @ 2016-11-17 18:07 fysola 阅读(3) 评论(0) <a href ="https://i.cnblogs.com/EditPosts.aspx?postid=6074862" rel="nofollow">编辑</a></div> <div class="clear"></div> <div class="postSeparator"></div> <div class="postTitle"> <a id="homepage1_HomePageDays_DaysList_ctl02_DayList_TitleUrl_1" class="postTitle2" href="http://www.cnblogs.com/fysola/p/6072883.html">JAVA基础知识之多线程——线程通信</a> </div> <div class="postCon"><div class="c_b_p_desc">摘要: 传统的线程通信 Object提供了三个方法wait(), notify(), notifyAll()在线程之间进行通信,以此来解决线程间执行顺序等问题。 wait():释放当前线程的同步监视控制器,并让当前线程进入阻塞状态,直到别的线程发出notify将该线程唤醒。 notify():唤醒在等待控制<a href="http://www.cnblogs.com/fysola/p/6072883.html" class="c_b_p_desc_readmore">阅读全文</a></div></div> <div class="clear"></div> <div class="postDesc">posted @ 2016-11-17 10:51 fysola 阅读(1) 评论(0) <a href ="https://i.cnblogs.com/EditPosts.aspx?postid=6072883" rel="nofollow">编辑</a></div> <div class="clear"></div> </div><div class="day"> <div class="dayTitle"> <a id="homepage1_HomePageDays_DaysList_ctl03_ImageLink" href="http://www.cnblogs.com/fysola/archive/2016/11/16.html">2016年11月16日</a> </div> <div class="postTitle"> <a id="homepage1_HomePageDays_DaysList_ctl03_DayList_TitleUrl_0" class="postTitle2" href="http://www.cnblogs.com/fysola/p/6071906.html">为Apache配置虚拟机Virtual Host</a> </div> <div class="postCon"><div class="c_b_p_desc">摘要: 配置虚拟机最少需要修改两个文件,即httpd-vhosts.conf 和 hosts (记住修改任何配置前先备份哦,这才是个好习惯) 第一步,修改httpd-vhosts.conf文件, 文件路径 \apache\conf\extra\ 在文件底部加入下面这段 第二步,修改hosts文件, 文件路径<a href="http://www.cnblogs.com/fysola/p/6071906.html" class="c_b_p_desc_readmore">阅读全文</a></div></div> <div class="clear"></div> <div class="postDesc">posted @ 2016-11-16 22:53 fysola 阅读(2) 评论(0) <a href ="https://i.cnblogs.com/EditPosts.aspx?postid=6071906" rel="nofollow">编辑</a></div> <div class="clear"></div> <div class="postSeparator"></div> <div class="postTitle"> <a id="homepage1_HomePageDays_DaysList_ctl03_DayList_TitleUrl_1" class="postTitle2" href="http://www.cnblogs.com/fysola/p/6071413.html">JAVA基础知识之多线程——线程同步</a> </div> <div class="postCon"><div class="c_b_p_desc">摘要: 线程安全问题 多个线程同时访问同一资源的时候有可能会出现信息不一致的情况,这是线程安全问题,下面是一个例子, Account.class , 定义一个Account模型 DrawThread.class ,定义一个取钱类,用来操作Account DrawTest.class , 写一个测试类 执行结<a href="http://www.cnblogs.com/fysola/p/6071413.html" class="c_b_p_desc_readmore">阅读全文</a></div></div> <div class="clear"></div> <div class="postDesc">posted @ 2016-11-16 20:33 fysola 阅读(4) 评论(0) <a href ="https://i.cnblogs.com/EditPosts.aspx?postid=6071413" rel="nofollow">编辑</a></div> <div class="clear"></div> <div class="postSeparator"></div> <div class="postTitle"> <a id="homepage1_HomePageDays_DaysList_ctl03_DayList_TitleUrl_2" class="postTitle2" href="http://www.cnblogs.com/fysola/p/6069016.html">JAVA基础知识之多线程——控制线程</a> </div> <div class="postCon"><div class="c_b_p_desc">摘要: join线程 在某个线程中调用其他线程的join()方法,就会使当前线程进入阻塞状态,直到被join线程执行完为止。join方法类似于wait, 通常会在主线程中调用别的线程的join方法,这样可以保证在所有的子线程执行结束之后在主线程中完成一些统一的步骤。下面是一个例子, 执行结果,可见当主线程中<a href="http://www.cnblogs.com/fysola/p/6069016.html" class="c_b_p_desc_readmore">阅读全文</a></div></div> <div class="clear"></div> <div class="postDesc">posted @ 2016-11-16 12:14 fysola 阅读(5) 评论(0) <a href ="https://i.cnblogs.com/EditPosts.aspx?postid=6069016" rel="nofollow">编辑</a></div> <div class="clear"></div> </div><div class="day"> <div class="dayTitle"> <a id="homepage1_HomePageDays_DaysList_ctl04_ImageLink" href="http://www.cnblogs.com/fysola/archive/2016/11/15.html">2016年11月15日</a> </div> <div class="postTitle"> <a id="homepage1_HomePageDays_DaysList_ctl04_DayList_TitleUrl_0" class="postTitle2" href="http://www.cnblogs.com/fysola/p/6066290.html">JAVA基础知识之多线程——线程的生命周期(状态)</a> </div> <div class="postCon"><div class="c_b_p_desc">摘要: 线程有五个状态,分别是新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)。 新建和就绪 程序使用new会新建一个线程,new出的对象跟普通对象一样,JVM会为其分配内存,初始化成员变量等,此时线程并没有运行,而是就是新建状态。 当线程对象调用s<a href="http://www.cnblogs.com/fysola/p/6066290.html" class="c_b_p_desc_readmore">阅读全文</a></div></div> <div class="clear"></div> <div class="postDesc">posted @ 2016-11-15 16:35 fysola 阅读(5) 评论(0) <a href ="https://i.cnblogs.com/EditPosts.aspx?postid=6066290" rel="nofollow">编辑</a></div> <div class="clear"></div> <div class="postSeparator"></div> <div class="postTitle"> <a id="homepage1_HomePageDays_DaysList_ctl04_DayList_TitleUrl_1" class="postTitle2" href="http://www.cnblogs.com/fysola/p/6065805.html">JAVA基础知识之多线程——三种实现多线程的方法及区别</a> </div> <div class="postCon"><div class="c_b_p_desc">摘要: 所有JAVA线程都必须是Thread或其子类的实例。 继承Thread类创建线程 步骤如下, 定义Thead子类并实现run()方法,run()是线程执行体 创建此子类实例对象,即创建了线程对象 调用线程对象的start()方法来启动线程 下面是一个例子, 执行结果, 可见thread-5和thre<a href="http://www.cnblogs.com/fysola/p/6065805.html" class="c_b_p_desc_readmore">阅读全文</a></div></div> <div class="clear"></div> <div class="postDesc">posted @ 2016-11-15 15:08 fysola 阅读(4) 评论(0) <a href ="https://i.cnblogs.com/EditPosts.aspx?postid=6065805" rel="nofollow">编辑</a></div> <div class="clear"></div> </div><div class="day"> <div class="dayTitle"> <a id="homepage1_HomePageDays_DaysList_ctl05_ImageLink" href="http://www.cnblogs.com/fysola/archive/2016/11/01.html">2016年11月1日</a> </div> <div class="postTitle"> <a id="homepage1_HomePageDays_DaysList_ctl05_DayList_TitleUrl_0" class="postTitle2" href="http://www.cnblogs.com/fysola/p/6021134.html">JAVA基础知识之Collections工具类</a> </div> <div class="postCon"><div class="c_b_p_desc">摘要: 排序操作 Collections提供以下方法对List进行排序操作 void reverse(List list):反转 void shuffle(List list),随机排序 void sort(List list),按自然排序的升序排序 void sort(List list, Compara<a href="http://www.cnblogs.com/fysola/p/6021134.html" class="c_b_p_desc_readmore">阅读全文</a></div></div> <div class="clear"></div> <div class="postDesc">posted @ 2016-11-01 21:43 fysola 阅读(6) 评论(0) <a href ="https://i.cnblogs.com/EditPosts.aspx?postid=6021134" rel="nofollow">编辑</a></div> <div class="clear"></div> </div><div class="topicListFooter"><div id="nav_next_page"><a href="http://www.cnblogs.com/fysola/default.html?page=2">下一页</a></div></div> </div><!--end: forFlow --> </div><!--end: mainContent 主体内容容器--> <div id="sideBar"> <div id="sideBarMain"> <div id="calendar"><div id="blog-calendar" style="display:none"></div><script type="text/javascript">loadBlogDefaultCalendar();</script></div> <div id="leftcontentcontainer"> <div id="blog-sidecolumn"></div><script type="text/javascript">loadBlogSideColumn();</script> </div> </div><!--end: sideBarMain --> </div><!--end: sideBar 侧边栏容器 --> <div class="clear"></div> </div><!--end: main --> <div class="clear"></div> <div id="footer"> <!--done-->Copyright &copy;2016 fysola </div><!--end: footer --></div><!--end: home 自定义的最大容器 --></body></html>
================================================
null--->[HTTP/1.1 200 OK]
Vary--->[Accept-Encoding]
Date--->[Sat, 19 Nov 2016 04:50:32 GMT]
Content-Length--->[14744]
Expires--->[Sat, 19 Nov 2016 04:50:42 GMT]
X-UA-Compatible--->[IE=10]
Last-Modified--->[Sat, 19 Nov 2016 04:50:32 GMT]
Connection--->[keep-alive]
Content-Type--->[text/html; charset=utf-8]
Cache-Control--->[private, max-age=10]
<!DOCTYPE html><html lang="zh-cn"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1" /><title>fysola - 博客园</title><link type="text/css" rel="stylesheet" href="/bundles/blog-common.css?v=Rdf1BBttS5_qVaET1myrajVTd62BSCCoJA9fZxGv1ZM1"/><link id="MainCss" type="text/css" rel="stylesheet" href="/skins/LessIsMore/bundle-LessIsMore.css?v=0F7NKnmmVYJDCp-KQ2sA7Dn1GREfDmWWyNjiJP9YrzE1"/><link id="mobile-style" media="only screen and (max-width: 768px)" type="text/css" rel="stylesheet" href="/skins/LessIsMore/bundle-LessIsMore-mobile.css?v=-559DaKqzQsHh-ZmNj3u85wntv3Lbkjpzbkkq1zkN_E1"/><link title="RSS" type="application/rss+xml" rel="alternate" href="http://www.cnblogs.com/fysola/rss"/><link title="RSD" type="application/rsd+xml" rel="EditURI" href="http://www.cnblogs.com/fysola/rsd.xml"/><link type="application/wlwmanifest+xml" rel="wlwmanifest" href="http://www.cnblogs.com/fysola/wlwmanifest.xml"/><script src="//common.cnblogs.com/script/jquery.js" type="text/javascript"></script> <script type="text/javascript">var currentBlogApp = 'fysola', cb_enable_mathjax=false;var isLogined=false;</script><script src="/bundles/blog-common.js?v=hH1lCMV8WaIu271Nx7jPuv36TENW9-RsSxziLxUpjtc1" type="text/javascript"></script></head><body><a name="top"></a><div id="home"><div id="header"> <div id="blogTitle"> <!--done--><div class="title"><a id="Header1_HeaderTitle" class="headermaintitle" href="http://www.cnblogs.com/fysola/">云中之歌</a></div><div class="subtitle">Practice like your performance</div> </div><!--end: blogTitle 博客的标题和副标题 --> <div id="navigator"> <ul id="navList"><li id="nav_sitehome"></li><li id="nav_myhome"><a id="blog_nav_myhome" class="menu" href="http://www.cnblogs.com/fysola/">首页</a></li><li id="nav_newpost"></li><li id="nav_contact"></li><li id="nav_rss"><a id="blog_nav_rss" class="menu" href="http://www.cnblogs.com/fysola/rss">订阅</a><!--<a id="blog_nav_rss_image" class="aHeaderXML" href="http://www.cnblogs.com/fysola/rss"><img src="//www.cnblogs.com/images/xml.gif" alt="订阅" /></a>--></li><li id="nav_admin"><a id="blog_nav_admin" class="menu" rel="nofollow" href="https://i.cnblogs.com/">管理</a></li></ul> <div class="blogStats"> </div><!--end: blogStats --> </div><!--end: navigator 博客导航栏 --></div><!--end: header 头部 --><div id="main"> <div id="mainContent"> <div class="forFlow"> <!--done--><div class="day"> <div class="dayTitle"> <a id="homepage1_HomePageDays_DaysList_ctl00_ImageLink" href="http://www.cnblogs.com/fysola/archive/2016/11/19.html">2016年11月19日</a> </div> <div class="postTitle"> <a id="homepage1_HomePageDays_DaysList_ctl00_DayList_TitleUrl_0" class="postTitle2" href="http://www.cnblogs.com/fysola/p/6080052.html">JAVA基础知识之网络编程——-网络基础(多线程下载,get,post)</a> </div> <div class="postCon"><div class="c_b_p_desc">摘要: 本文主要介绍java.net下为网络编程提供的一些基础包,InetAddress代表一个IP协议对象,可以用来获取IP地址,Host name之类的信息。URL和URLConnect可以用来访问web资源,URLDecode和URLEncode用来转换字符串。 本文会写两个例子来演示java网络编程<a href="http://www.cnblogs.com/fysola/p/6080052.html" class="c_b_p_desc_readmore">阅读全文</a></div></div> <div class="clear"></div> <div class="postDesc">posted @ 2016-11-19 10:42 fysola 阅读(1) 评论(0) <a href ="https://i.cnblogs.com/EditPosts.aspx?postid=6080052" rel="nofollow">编辑</a></div> <div class="clear"></div> </div><div class="day"> <div class="dayTitle"> <a id="homepage1_HomePageDays_DaysList_ctl01_ImageLink" href="http://www.cnblogs.com/fysola/archive/2016/11/18.html">2016年11月18日</a> </div> <div class="postTitle"> <a id="homepage1_HomePageDays_DaysList_ctl01_DayList_TitleUrl_0" class="postTitle2" href="http://www.cnblogs.com/fysola/p/6076118.html">JAVA基础知识之多线程——线程池</a> </div> <div class="postCon"><div class="c_b_p_desc">摘要: 线程池概念 操作系统或者JVM创建一个线程以及销毁一个线程都需要消耗CPU资源,如果创建或者销毁线程的消耗源远远小于执行一个线程的消耗,则可以忽略不计,但是基本相等或者大于执行线程的消耗,而且需要创建大批量这种线程的话,CPU将资源将会大量消耗在创建线程和销毁线程上,这是不能接受的,因此我们需要一个<a href="http://www.cnblogs.com/fysola/p/6076118.html" class="c_b_p_desc_readmore">阅读全文</a></div></div> <div class="clear"></div> <div class="postDesc">posted @ 2016-11-18 00:33 fysola 阅读(2) 评论(0) <a href ="https://i.cnblogs.com/EditPosts.aspx?postid=6076118" rel="nofollow">编辑</a></div> <div class="clear"></div> </div><div class="day"> <div class="dayTitle"> <a id="homepage1_HomePageDays_DaysList_ctl02_ImageLink" href="http://www.cnblogs.com/fysola/archive/2016/11/17.html">2016年11月17日</a> </div> <div class="postTitle"> <a id="homepage1_HomePageDays_DaysList_ctl02_DayList_TitleUrl_0" class="postTitle2" href="http://www.cnblogs.com/fysola/p/6074862.html">JAVA基础知识之多线程——线程组和未处理异常</a> </div> <div class="postCon"><div class="c_b_p_desc">摘要: 线程组 Java中的ThreadGroup类表示线程组,在创建新线程时,可以通过构造函数Thread(group...)来指定线程组。 线程组具有以下特征 如果没有显式指定线程组,则新线程属于默认线程组,默认情况下,与创建线程所在的组相同 一旦确定了线程所在线程组之后,不允许更改线程组,直到线程死亡<a href="http://www.cnblogs.com/fysola/p/6074862.html" class="c_b_p_desc_readmore">阅读全文</a></div></div> <div class="clear"></div> <div class="postDesc">posted @ 2016-11-17 18:07 fysola 阅读(3) 评论(0) <a href ="https://i.cnblogs.com/EditPosts.aspx?postid=6074862" rel="nofollow">编辑</a></div> <div class="clear"></div> <div class="postSeparator"></div> <div class="postTitle"> <a id="homepage1_HomePageDays_DaysList_ctl02_DayList_TitleUrl_1" class="postTitle2" href="http://www.cnblogs.com/fysola/p/6072883.html">JAVA基础知识之多线程——线程通信</a> </div> <div class="postCon"><div class="c_b_p_desc">摘要: 传统的线程通信 Object提供了三个方法wait(), notify(), notifyAll()在线程之间进行通信,以此来解决线程间执行顺序等问题。 wait():释放当前线程的同步监视控制器,并让当前线程进入阻塞状态,直到别的线程发出notify将该线程唤醒。 notify():唤醒在等待控制<a href="http://www.cnblogs.com/fysola/p/6072883.html" class="c_b_p_desc_readmore">阅读全文</a></div></div> <div class="clear"></div> <div class="postDesc">posted @ 2016-11-17 10:51 fysola 阅读(1) 评论(0) <a href ="https://i.cnblogs.com/EditPosts.aspx?postid=6072883" rel="nofollow">编辑</a></div> <div class="clear"></div> </div><div class="day"> <div class="dayTitle"> <a id="homepage1_HomePageDays_DaysList_ctl03_ImageLink" href="http://www.cnblogs.com/fysola/archive/2016/11/16.html">2016年11月16日</a> </div> <div class="postTitle"> <a id="homepage1_HomePageDays_DaysList_ctl03_DayList_TitleUrl_0" class="postTitle2" href="http://www.cnblogs.com/fysola/p/6071906.html">为Apache配置虚拟机Virtual Host</a> </div> <div class="postCon"><div class="c_b_p_desc">摘要: 配置虚拟机最少需要修改两个文件,即httpd-vhosts.conf 和 hosts (记住修改任何配置前先备份哦,这才是个好习惯) 第一步,修改httpd-vhosts.conf文件, 文件路径 \apache\conf\extra\ 在文件底部加入下面这段 第二步,修改hosts文件, 文件路径<a href="http://www.cnblogs.com/fysola/p/6071906.html" class="c_b_p_desc_readmore">阅读全文</a></div></div> <div class="clear"></div> <div class="postDesc">posted @ 2016-11-16 22:53 fysola 阅读(2) 评论(0) <a href ="https://i.cnblogs.com/EditPosts.aspx?postid=6071906" rel="nofollow">编辑</a></div> <div class="clear"></div> <div class="postSeparator"></div> <div class="postTitle"> <a id="homepage1_HomePageDays_DaysList_ctl03_DayList_TitleUrl_1" class="postTitle2" href="http://www.cnblogs.com/fysola/p/6071413.html">JAVA基础知识之多线程——线程同步</a> </div> <div class="postCon"><div class="c_b_p_desc">摘要: 线程安全问题 多个线程同时访问同一资源的时候有可能会出现信息不一致的情况,这是线程安全问题,下面是一个例子, Account.class , 定义一个Account模型 DrawThread.class ,定义一个取钱类,用来操作Account DrawTest.class , 写一个测试类 执行结<a href="http://www.cnblogs.com/fysola/p/6071413.html" class="c_b_p_desc_readmore">阅读全文</a></div></div> <div class="clear"></div> <div class="postDesc">posted @ 2016-11-16 20:33 fysola 阅读(4) 评论(0) <a href ="https://i.cnblogs.com/EditPosts.aspx?postid=6071413" rel="nofollow">编辑</a></div> <div class="clear"></div> <div class="postSeparator"></div> <div class="postTitle"> <a id="homepage1_HomePageDays_DaysList_ctl03_DayList_TitleUrl_2" class="postTitle2" href="http://www.cnblogs.com/fysola/p/6069016.html">JAVA基础知识之多线程——控制线程</a> </div> <div class="postCon"><div class="c_b_p_desc">摘要: join线程 在某个线程中调用其他线程的join()方法,就会使当前线程进入阻塞状态,直到被join线程执行完为止。join方法类似于wait, 通常会在主线程中调用别的线程的join方法,这样可以保证在所有的子线程执行结束之后在主线程中完成一些统一的步骤。下面是一个例子, 执行结果,可见当主线程中<a href="http://www.cnblogs.com/fysola/p/6069016.html" class="c_b_p_desc_readmore">阅读全文</a></div></div> <div class="clear"></div> <div class="postDesc">posted @ 2016-11-16 12:14 fysola 阅读(5) 评论(0) <a href ="https://i.cnblogs.com/EditPosts.aspx?postid=6069016" rel="nofollow">编辑</a></div> <div class="clear"></div> </div><div class="day"> <div class="dayTitle"> <a id="homepage1_HomePageDays_DaysList_ctl04_ImageLink" href="http://www.cnblogs.com/fysola/archive/2016/11/15.html">2016年11月15日</a> </div> <div class="postTitle"> <a id="homepage1_HomePageDays_DaysList_ctl04_DayList_TitleUrl_0" class="postTitle2" href="http://www.cnblogs.com/fysola/p/6066290.html">JAVA基础知识之多线程——线程的生命周期(状态)</a> </div> <div class="postCon"><div class="c_b_p_desc">摘要: 线程有五个状态,分别是新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)。 新建和就绪 程序使用new会新建一个线程,new出的对象跟普通对象一样,JVM会为其分配内存,初始化成员变量等,此时线程并没有运行,而是就是新建状态。 当线程对象调用s<a href="http://www.cnblogs.com/fysola/p/6066290.html" class="c_b_p_desc_readmore">阅读全文</a></div></div> <div class="clear"></div> <div class="postDesc">posted @ 2016-11-15 16:35 fysola 阅读(5) 评论(0) <a href ="https://i.cnblogs.com/EditPosts.aspx?postid=6066290" rel="nofollow">编辑</a></div> <div class="clear"></div> <div class="postSeparator"></div> <div class="postTitle"> <a id="homepage1_HomePageDays_DaysList_ctl04_DayList_TitleUrl_1" class="postTitle2" href="http://www.cnblogs.com/fysola/p/6065805.html">JAVA基础知识之多线程——三种实现多线程的方法及区别</a> </div> <div class="postCon"><div class="c_b_p_desc">摘要: 所有JAVA线程都必须是Thread或其子类的实例。 继承Thread类创建线程 步骤如下, 定义Thead子类并实现run()方法,run()是线程执行体 创建此子类实例对象,即创建了线程对象 调用线程对象的start()方法来启动线程 下面是一个例子, 执行结果, 可见thread-5和thre<a href="http://www.cnblogs.com/fysola/p/6065805.html" class="c_b_p_desc_readmore">阅读全文</a></div></div> <div class="clear"></div> <div class="postDesc">posted @ 2016-11-15 15:08 fysola 阅读(4) 评论(0) <a href ="https://i.cnblogs.com/EditPosts.aspx?postid=6065805" rel="nofollow">编辑</a></div> <div class="clear"></div> </div><div class="day"> <div class="dayTitle"> <a id="homepage1_HomePageDays_DaysList_ctl05_ImageLink" href="http://www.cnblogs.com/fysola/archive/2016/11/01.html">2016年11月1日</a> </div> <div class="postTitle"> <a id="homepage1_HomePageDays_DaysList_ctl05_DayList_TitleUrl_0" class="postTitle2" href="http://www.cnblogs.com/fysola/p/6021134.html">JAVA基础知识之Collections工具类</a> </div> <div class="postCon"><div class="c_b_p_desc">摘要: 排序操作 Collections提供以下方法对List进行排序操作 void reverse(List list):反转 void shuffle(List list),随机排序 void sort(List list),按自然排序的升序排序 void sort(List list, Compara<a href="http://www.cnblogs.com/fysola/p/6021134.html" class="c_b_p_desc_readmore">阅读全文</a></div></div> <div class="clear"></div> <div class="postDesc">posted @ 2016-11-01 21:43 fysola 阅读(6) 评论(0) <a href ="https://i.cnblogs.com/EditPosts.aspx?postid=6021134" rel="nofollow">编辑</a></div> <div class="clear"></div> </div><div class="topicListFooter"><div id="nav_next_page"><a href="http://www.cnblogs.com/fysola/default.html?page=2">下一页</a></div></div> </div><!--end: forFlow --> </div><!--end: mainContent 主体内容容器--> <div id="sideBar"> <div id="sideBarMain"> <div id="calendar"><div id="blog-calendar" style="display:none"></div><script type="text/javascript">loadBlogDefaultCalendar();</script></div> <div id="leftcontentcontainer"> <div id="blog-sidecolumn"></div><script type="text/javascript">loadBlogSideColumn();</script> </div> </div><!--end: sideBarMain --> </div><!--end: sideBar 侧边栏容器 --> <div class="clear"></div> </div><!--end: main --> <div class="clear"></div> <div id="footer"> <!--done-->Copyright &copy;2016 fysola </div><!--end: footer --></div><!--end: home 自定义的最大容器 --></body></html>

第二个例子,写一个程序用来模拟多线程下载。

本例中用到的技术有,
多线程——多个线程同时读文件写文件,可以加快下载速度,

线程池——在本例中线程池不是必须,甚至是多余,只不过是为了演示线程池的简单用法

断点下载/上传——用RandomAccessFile的seek方法可以直接从某个中间位置读取或者写入文件

URLConnection——建立网络连接,读取网络数据的基本方法

ioString——网络IO,建立在URLConnection上的网络IO stream

有几个关键点值得注意,

  • 一是使用了URL的实例建立了一个httpURLConnection实例, 通过这个实例的getInputStream(继承自父类)可以返回一个instream对象进行读数据操作
  • 二是由于是多线程从远程读文件,每个文件将读取一块文件,文件大小按线程数平分,单个线程文件块 = 文件总大小 / 线程总数,使用RandomAccessFile的方式可以按指定位置读写文件
  • 使用RandomAccessFile的seek方法,计算每个线程下载的起始位置
  • 使用inStream的skip方法计算每次从网络读取数据的位置

具体实现如下, 先写一个工具类DownUtil, 其中写了一个内部线程类download,还定义了一个线程池,将多个线程类的实例提交给线程池管理,

 package network;

 import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; public class DownUtil {
//下载资源路径
private String path;
private String targetFile;
//线程数量
private int threadNum;
//下载线程对象
private DownThread[] threads;
//下载文件总大小
private int fileSize;
//定义一个线程池,在构造函数中初始化成具体类型的线程池
ExecutorService pool; public DownUtil(String path, String targetFile, int threadNum) {
this.path = path;
this.targetFile = targetFile;
this.threadNum = threadNum;
this.threads = new DownThread[threadNum];
this.pool = Executors.newFixedThreadPool(threadNum);
} public void download() throws IOException {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setConnectTimeout(5*1000);
conn.setRequestMethod("GET");
conn.setRequestProperty("accept", "*/*");
/*
conn.setRequestProperty("Accept", "image/gif, image/jpg, image/png, "
+ "application/x-shockwave-flash, application/xam+xml, "
+ "application/vnd.ms-xpsdocument, application/x-ms-xbap, "
+ "application/x-ms-application, application/vnd.ms-excel, "
+ "application/vnd.ms-powerpoint, application/msword, **");
*/
conn.setRequestProperty("Charset", "multipart/form-data");
conn.setRequestProperty("Connection", "Keep-Alive");
//得到文件大小
fileSize = conn.getContentLength();
conn.disconnect();
//每个线程要下载的文件部分的大小
int currentPartSize = fileSize / threadNum + 1;
RandomAccessFile file = new RandomAccessFile(targetFile , "rw");
file.setLength(fileSize);
file.close();
for ( int i = 0; i < threadNum; i++ ) {
// 计算每个线程下载的开始位置
int startPos = i * currentPartSize;
//每个线程使用一个RandomAccessFile进行下载
RandomAccessFile currentPart = new RandomAccessFile(targetFile, "rw");
//定位该线程的下载位置
currentPart.seek(startPos);
//创建下载线程
threads[i] = new DownThread(startPos, currentPartSize, currentPart);
//将单个线程提交到线程池,线程会启动
pool.submit(threads[i]);
}
pool.shutdown();
} //获取下载的完成百分比
public double getCompleteRate() {
//统计多个线程已经下载的总大小
int sumSize = 0;
for (int i = 0; i< threadNum; i++) {
sumSize += threads[i].length;
}
return sumSize * 1.0 / fileSize ;
} private class DownThread implements Runnable {
//当前线程的下载位置
private int startPos;
//当前线程负责下载的大小
private int currentParSize;
//当前线程需要下载的文件块
private RandomAccessFile currentPart;
//该线程一下载字节数
public int length; public DownThread(int startPos, int currentPartSize,
RandomAccessFile currentPart) {
this.startPos = startPos;
this.currentParSize = currentPartSize;
this.currentPart = currentPart;
} public void run() {
try {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setConnectTimeout(5*1000);
conn.setRequestMethod("GET");
conn.setRequestProperty("accept", "*/*");
/*
conn.setRequestProperty("Accept", "image/gif, image/jpg, image/png, "
+ "application/x-shockwave-flash, application/xam+xml, "
+ "application/vnd.ms-xpsdocument, application/x-ms-xbap, "
+ "application/x-ms-application, application/vnd.ms-excel, "
+ "application/vnd.ms-powerpoint, application/msword, **");
*/
//conn.setContentType(conn.getContentType());
conn.setRequestProperty("Charset", "UTF-8");
conn.setRequestProperty("Connection", "Keep-Alive");
InputStream inStream = conn.getInputStream();
//跳过startPos之前的内容
inStream.skip(this.startPos);
byte[] buffer = new byte[1024];
int hasRead = 0;
// 读取网络数据,写入本地文件
while (length < currentParSize && (hasRead = inStream.read(buffer)) != -1) {
currentPart.write(buffer, 0 ,hasRead);
length += hasRead;
}
currentPart.close();
inStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}

在主程序中初始化工具类DownUtil,然后每隔固定时间去检查一次下载进度,

 package network;

 import java.io.IOException;

 public class MultiThreadDown {
public static void main(String[] args) throws IOException, InterruptedException {
// 初始化DownUtil
final DownUtil downUtil = new DownUtil(
"http://sw.bos.baidu.com/sw-search-sp/software/7d662d80a3d85/npp_7.2_Installer.exe",
"notepad.exe",4);
//开始下载
downUtil.download();
Thread monitor = new Thread(new Runnable(){ @Override
public void run() {
while(downUtil.getCompleteRate() <= 1) {
//每隔1秒查看一次完成进度
System.out.println("已完成:"+downUtil.getCompleteRate());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} });
monitor.start();
monitor.join();
System.out.println("下载完成");
}
}

程序是以下载一个QQ程序为例, 下面是执行结果,

 已完成:0.0
已完成:0.11293072177355859
已完成:0.278329429397341
已完成:0.4422385019008841
已完成:0.5001718809754122
已完成:0.5001718809754122
已完成:0.6514949778417655
已完成:0.9376444643781072
下载完成

本例中,如果调整线程个数,经过测试,线程数越多,下载反而越慢,估计是因为创建IO时间占用太多,毕竟下载一个文件本来就不用多久。 看来对于小文件下载但是单线程快。