一、线程
1、基本概念
线程就是程序执行中的一个执行路径(子任务)。
多线程是指程序中包含多条执行路径。也是指在一个进程中可同时执行两个或两个以上的线程。
2、进程与线程:1.进程每个程序都至少有一个进程,每个进程中都至少有一个线程。
2.线程是进程的一个实体。
3.进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。线程自己基本上不拥有系统资源。
4.一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行
TIPS:直白地说,线程就是程序的执行路径。进程就是来管理这些进程的(线程需要资源需要从进程获取)。
3、JAVA线程的启动:1).JAVA程序启动时,会有一个由主方法(main)来定义的线程。这个线程帮助我们的程序依据主函数中的语句顺序执行代码。
2.)我们可以通过java.lang.Thread或者java.lang.Runnable来启动一个线程。
例1:通过继承Thread类来实现线程的启动
package com.java.group.thread;
public class ThreadTest{
public static void main(String[] args) {
//获取我们MyThread对象
MyThread mt = new MyThread();
//开启线程
mt.start();
}
}
class MyThread extends Thread{
@Override
public void run(){
System.out.println("线程中需要执行的代码!");
}
}
例2、通过继承Runnable接口来实现线程的启动
package com.java.group.thread;
public class RunnableTest{
public static void main(String[] args) {
//创建MyRunnable对象
MyRunnable mr = new MyRunnable();
//通过Thread对象来启动线程
new Thread(mr).start();
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("代码块!");
}
}
4、常用的方法
isAlive(),线程是否还在运行.
getPriority(),获取当前线程优先级
setPriority(),线程优先级设置,值在10-1之间。
sleep(),休眠,指定的时间是(毫秒)或者(毫秒,纳秒)
join(),等待指定线程 执行完毕。(相当于与执行线程合并)
yield(),让出cpu,暂停当前正在执行的线程对象,并执行其他线程
start(), 使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
5、线程同步
(1)wait():使一个线程处于等待状态,并且释放所持有的对象的lock。
(2)sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉
InterruptedException异常。
(3)notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的
唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。
(4)notityAll ():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁。
6、Wait与Sleep的区别:
wait 时,别的线程可以访问锁定对象
调用wait方法的时候必须锁定该对象
sleep时,别的线程也不可以访问锁定对象而是让它们竞争
7、synchronized关键字1. synchronized 方法:在方法声明中加入 synchronized关键字来声明 synchronized 方法
好处:声明为 synchronized 的成员函数中至多只有一个处于可执行状态,避免房屋内冲突
2. synchronized 块:通过 synchronized关键字来声明synchronized 块。
好处:对任意代码块,可任意指定上锁的对象,灵活性较高。
二、网络编程
1、网络编程就是两个或多个设备之间的数据交换,其实更具体的说,网络编程就是两个或多个程序之间的数据交换,和普通的单机程序相比,网络程序最大的不同就是需要交
换数据的程序运行在不同的计算机上,这样就造成了数据交换的复杂。虽然通过IP地址和端口可以找到网络上运行的一个程序,但是如果需要进行网络编程,则还需要了解网络
通讯的过程。
2、通讯方式
在现有的网络中,网络通讯主要有两种。
1)TCP(传输控制协议)方式
建立连接,形成传输数据的通道。
在连接中进行大数据量传输
通过三次握手完成连接,是可靠协议
必须建立连接,效率会稍低
2)UDP(用户数据报协议)方式
将数据及源和目的封装成数据包中,不需要建立连接
每个数据包的大小在限制在64k内
因无连接,是不可靠协议
不需要建立连接,速度快
3、Socket
Socket就是为网络服务提供的一种机制。基于TCP/IP协议
通信的两端都有Socket。
网络通信其实就是Socket间的通信
数据在两个Socket间通过IO传输
例:
package com.java.group.clienttoclient;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
public class SocketTest {
/**
* client
* @param args
*/
public static void main(String[] args) throws UnknownHostException {
Socket socket = null;
final Scanner scanner = new Scanner(System.in);
try {
//创建socket对象
socket = new Socket("177.16.1.107",2333);
//接收数据和发送数据
//获取输入流
final BufferedReader bReader = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
//创建输出流
final PrintWriter pw = new PrintWriter(socket.getOutputStream());
final Object obj = new Object();
//写线程
new Thread(new Runnable(){
@Override
public void run(){
boolean isGoOn = true;
while(isGoOn){
talkMsg(pw,scanner);
}
}
}).start();
//读线程
new Thread(new Runnable(){
@Override
public void run(){
boolean isGoOn = true;
while(isGoOn){
try {
String msg = bReader.readLine();
System.out.println("服务器:"+msg);
if("exit".equals(msg)){
obj.notifyAll();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}).start();
synchronized(obj){
obj.wait();
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally{
try {
scanner.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 发消息
* @param pw
* @param scanner
*/
public static void talkMsg(PrintWriter pw, Scanner scanner){
String msg = null;
msg = scanner.nextLine();
pw.println(msg);
pw.flush();
}
}
4、UDP传输
例:发出广播
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
public class UDPSend {
public static void main(String[] args) {
try {
//创建套接字,udp创建套接字,不用在意别人的ip地址和端口
DatagramSocket socket = new DatagramSocket() ;
//
byte[] buf = "大家好!!!!".getBytes() ;
//创建数据包,要给谁发信息,就在信息里带上她的ip和端口
/**
* UDP广播
* */
DatagramPacket p = new DatagramPacket(buf, 0, buf.length, InetAddress.getByName("177.16.1.255"), 15151);
//发送数据包
socket.send(p) ;
//关闭
socket.close() ;
} catch (SocketException e) {
e.printStackTrace();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
接收广播
package com.feicui.group1.udp;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import javax.xml.crypto.Data;
public class UDPReceive {
public static void main(String[] args) {
try {
//创建UDP套接字,如果要收信息,记得绑定端口号
DatagramSocket socket = new DatagramSocket(15151) ;
//创建数据包
DatagramPacket p = new DatagramPacket(new byte[1024], 1024) ;
//接受数据到一个数据包中
socket.receive(p) ;
//打印接收到的信息
System.out.println("接受到信息:"+new String(p.getData(),p.getOffset(),p.getLength())) ;
//关闭
socket.close() ;
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
5、HTTP协议
1)HTTP请求:请求行+若干消息头+空行+请求内容
请求行:
请求方式+请求资源+协议版本
GET /books/java.html HTTP/1.1
请求行用于描述客户端的请求方式,请求的资源名称,以及使用的HTTP
协议版本号。
2)请求方法
GET 请求获取Request-URI 所标识的资源;
POST 在Request-URI 所标识的资源后附加新的数据;
HEAD 请求获取由Request-URI 所标识的资源的响应消息报头;
PUT 请求服务器存储一个资源,并用Request-URI 作为其标识;
DELETE 请求服务器删除Request-URI 所标识的资源;
TRACE 请求服务器回送收到的请求信息,主要用于测试或诊断;
CONNECT 保留将来使用;
OPTIONS 请求查询服务器的性能,或者查询与资源相关的选项和需求。
最常用的是GET和POST方法。
3)常用请求头
Accept:text/html,image/* 告知服务器,客户机支持的数据类型
Accept-Charset:ISO-8859-1 告知服务器,客户机支持的字符编码
Accept-Encoding:gzip,compress 告知服务器,客户机支持的数据压缩格式
Accept-Language:en-us,zh-cn 告知服务器,客户机的语言环境
Host:www.baidu.com:80 告知服务器,客户机想访问的主机名
if-Modified-Since:Sat,03 Jan 2015 05:38:18 GMT 资源缓存时间
Referer:http://www.baidu.com/xxx.jsp 从哪个资源访问服务器
User-Agent:Mozilla/4.0(compatible;MSIE5.5;WindowsNT 5.1) Cookie告知服务器,客户机的软件环境
Connection:close/Keep-Alive 告知服务器,完成请求后的连接状态。
Date:Mon,28 Jul 2014 10:09:05 GMT 请求时间
4)常见消息头
Location:配合302,实现请求重定向。
Server:服务器类型。
Content-Encoding:服务器的压缩格式。
Content-Length:数据的大小。
Content-Language:语言环境
Content-Type:会送数据的类型(image/bmp,video/*,text/html)
Refresh:隔多长时间刷新一次。
Content-Disposition:以下载方式打开此数据内容。
Transfer-Encoding:数据传输格式(ETag,Chunked)
Expires:数据缓存时间。-1或0为不缓存。
5、多线程下载实现过程
1)首先得到下载的文件的长度,然后设置本地文件的长度
2)根据文件长度和线程数计算每个线程下载的数据长度和下载的位置
3)使用HTTP的Range头字段指定每条线程从文件的什么位置开始下载,下载到什么位置。
4)保存文件,使用RandomAccessFile类指定每条线程从本地文件的什么位置开始写入数据。
例:多线程下载
package com.feicui.group1.http;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
public class MutiThreadDownload {
public static void main(String[] args){
try {
//1.获取文件总大小
int fileMaxLength = getTotalSize() ;
//2.定义线程数量
int threadNum = 3 ;
//3.计算每个线程所需下载量,能除尽就用这个,不行就每个线程下载量加1
int avgLength = (fileMaxLength%threadNum)==0?fileMaxLength/threadNum:(fileMaxLength/threadNum+1) ;
//上面的方法可能会多下载1-2个字节
avgLength = fileMaxLength/threadNum ;
int lastAddbytes = fileMaxLength%threadNum ;
//4.指定开始位置
int startOne = 0 ;
int startTwo = startOne + avgLength ;
int startThree = startTwo + avgLength ;
System.out.println("startOne="+startOne);
System.out.println("startTwo="+startTwo);
System.out.println("startThree="+startThree);
//5.创建一个文件
File file = new File("E:/haha.rar") ;
new DownLoadThread(startOne,avgLength,file).start() ;
new DownLoadThread(startTwo,avgLength,file).start() ;
new DownLoadThread(startThree,(avgLength+lastAddbytes),file).start() ;
} catch (IOException e) {
e.printStackTrace();
}
}
public static int getTotalSize() throws IOException {
//URL
URL url = new URL("http://177.16.1.116:8080/manager/adt20130729.zip") ;
//连接服务器
HttpURLConnection connect = (HttpURLConnection) url.openConnection() ;
//请求,获取响应
return connect.getContentLength() ;
}
}
class DownLoadThread extends Thread{
int dLength ;
File file ;
int offset ;
public DownLoadThread(int offset, int dLength, File file){
this.dLength = dLength ;
this.file = file ;
this.offset = offset ;
}
@Override
public void run(){
InputStream is = null ;
RandomAccessFile raf = null ;
try {
//URL
URL url;
url = new URL("http://177.16.1.116:8080/manager/adt20130729.zip");
//连接服务器
HttpURLConnection connect = (HttpURLConnection) url.openConnection() ;
//设置消息头
connect.setRequestProperty("Range", "bytes="+offset+"-"+ (offset+dLength)) ;
//请求服务器,获取响应
is = connect.getInputStream() ;
//设置文件输出流
raf = new RandomAccessFile(file, "rwd") ;
//TODO
raf.seek(offset) ;
//流操作
byte[] buf = new byte[1024] ;
int readLength = 0 ;
while((readLength = is.read(buf))!=-1){
raf.write(buf, 0, readLength) ;
}
} catch (IOException e) {
e.printStackTrace();
}finally{
try{
if(is!=null)is.close() ;
if(raf!=null)raf.close() ;
}catch(IOException e){
e.printStackTrace() ;
}
}
}
}