等待唤醒机制:
了解等待唤醒机制之前,有必要搞清一个概念——线程之间的通信:多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同!通过一定的手段使各个线程能有效的利用资源,而这种手段即等待唤醒机制!
Wait():等待,将正在执行的线程释放其执行资格和执行权,并存储到线程池中!
Notify():唤醒,唤醒线程池中被wait()的线程,一次唤醒一个,而且是任意的!
NotifyAll(): 唤醒全部:可以将线程池中的所有wait() 线程都唤醒!
其实,所谓唤醒的意思就是让线程池中的线程具备执行资格!必须注意的是,这些方法都是在同步中才有效,同时这些方法在使用时必须标明所属锁,这样才可以明确出这些方法操作的到底是哪个锁上的线程,锁对象调用这些方法!
必要条件:
在同步中才有效(Synchronized)!
必须标明所属锁,相互通信的线程保证锁的唯一性!
使用锁对象调用Wait(),Notify(),NotifyAll()方法!
线程通信示例代码:
package com.oracle.duoxianchengshangwu; public class Resourse { public String name; public String sex; public boolean flag=false; } package com.oracle.duoxianchengshangwu; public class Input implements Runnable { private Resourse re; public Input(Resourse re) { super(); this.re = re; } //奇偶数实现交替赋值: private int i=0; public void run() { while(true){ synchronized(re){ if(re.flag){ try { re.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } if(i%2==0){ re.name="zhangsan"; re.sex="nan"; }else{ re.name="lisi"; re.sex="nv"; } re.flag=true; re.notify(); } ++i; } } } package com.oracle.duoxianchengshangwu; public class Output implements Runnable { private Resourse re; public Output(Resourse re) { super(); this.re = re; } public void run() { while(true){ synchronized(re){ if(!re.flag){ try { re.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(re.name+"……"+re.sex); re.flag=false; re.notify(); } } } } package com.oracle.duoxianchengshangwu; public class Test { public static void main(String[] args) { Resourse re=new Resourse(); Input in=new Input(re); Output out=new Output(re); Thread tin=new Thread(in); Thread tout=new Thread(out); tin.start(); tout.start(); } }
网络通讯协议:
应用最广泛的应该是TCP/IP和UDP协议!
TCP/IP协议中的四层分别是应用层,传输层,网络层和链路层,每层分别负责不同的通信功能,接下来针对这四层进行详细地讲解:
链路层:链路层是用于定义物理传输通道,通常是对某些网络连接设备的驱动协议,例如针对光纤,网线提供的驱动!
网络层:网络层是整个TCP/IP协议的核心,它主要用于将传输的数据进行分组,将分组数据发送到目标计算机或者网络!
传输层:主要使网络程序进行通信,在进行网络通信时,可以采用TCP协议,也可以采用UDP协议!
应用层:主要负责应用程序的协议,例如HTTP协议,FTP协议等!
要想使网络中的计算机能够进行通信,必须为每台计算机指定一个标识号,通过这个标识号来指定接受数据的计算机或者发送数据的计算机!
IP地址规范:首个字节不能是0且不能超过255,后面3个字节可以是0,不能超过255!
通过IP地址可以连接到指定计算机,但如果想访问目标计算机中的某个应用程序,还需要指定端口号!在计算机中,不同的应用程序是通过端口号区分的!端口号是用两个字节(16位的二进制数)表示的,它的取值范围是0~65535,其中,0~1023之间的端口号用于一些知名的网络服务和应用,用户的普通应用程序需要使用1024以上的端口号,从而避免端口号被另外一个应用或服务所占用!
JDK中提供了一个InetAdderss类,该类用于封装一个IP地址,并提供了一系列与IP地址相关的方法,下表中列出了InetAddress类的一些常用方法。
UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接!简单来说,当一台计算机向另外一台计算机发送数据时,发送端不会确认接收端是否存在,就会发出数据,同样接收端在收到数据时,也不会向发送端反馈是否收到数据!
由于使用UDP协议消耗资源小,通信效率高,所以通常都会用于音频,视频和普通数据的传输例如视频会议都使用UDP协议,因为这种情况即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响!大部分是TCP/IP和UDP结合起来使用的!
但是在使用UDP协议传送数据时,由于UDP的面向无连接性,不能保证数据的完整性,因此在传输重要数据时不建议使用UDP协议!UDP传输数据被限制在64K以内!
TCP协议三次握手:
客户端向服务器端发出连接请求,等待服务器确认,第二次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求,第三次握手,客户端再次向服务器端发送确认信息,确认连接。
UDP通信:
用于接收端的方式:
使用该构造方法在创建DatagramPacket对象时,指定了封装数据的字节数组和数据的大小,没有指定IP地址和端口号。很明显,这样的对象只能用于接收端,不能用于发送端。因为发送端一定要明确指出数据的目的地(ip地址和端口号),而接收端不需要明确知道数据的来源,只需要接收到数据即可。
DatagramSocket:
发送端:
该构造方法用于创建发送端的DatagramSocket对象!
该构造方法既可用于创建接收端的DatagramSocket对象,又可以创建发送端的DatagramSocket对象,在创建接收端的DatagramSocket对象时,必须要指定一个端口号,这样就可以监听指定的端口。
UDP网络程序图解:
示例代码:
package com.oracle.duoxianchengxiawu; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; //接收端: public class UDPRecieve { public static void main(String[] args) throws IOException { //明确端口号: DatagramSocket Ds=new DatagramSocket(60000); //创建接收数据的字节数组: byte[] bytes=new byte[1024]; //创建接收的数据包: DatagramPacket Dp=new DatagramPacket(bytes,bytes.length); //接收数据包: Ds.receive(Dp); //获取接收包上的数据: int len=Dp.getLength(); String Ip=Dp.getAddress().getHostAddress(); int port=Dp.getPort(); System.out.println("IP地址为:"+Ip+"端口号为:"+port+"发送的内容为:"+new String(bytes,0,len)); Ds.close(); } } package com.oracle.duoxianchengxiawu; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; //发送端: public class UDPSend { public static void main(String[] args) throws IOException { //打包: //明确数据: byte[] bytes="你好么?".getBytes(); //明确目的地的IP地址: InetAddress inet=InetAddress.getByName("127.0.0.1"); //装包: DatagramPacket Dp=new DatagramPacket(bytes,bytes.length,inet,60000); //创建快递公司: DatagramSocket Ds=new DatagramSocket(); //发送: Ds.send(Dp); //释放资源: Ds.close(); } }
简化版本的UDP通信代码:
package com.oracle.duoxianchengxiawu2; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; public class UDPRecieve { public static void main(String[] args) throws IOException { //明确端口号: DatagramSocket Ds=new DatagramSocket(6666); //创建接收数据的字节数组: byte[] bytes=new byte[1024]; while(true){ //创建接收的数据包: DatagramPacket Dp=new DatagramPacket(bytes,bytes.length); //接收数据包: Ds.receive(Dp); //获取接收包上的数据: int len=Dp.getLength(); String Ip=Dp.getAddress().getHostAddress(); int port=Dp.getPort(); System.out.println("IP地址为:"+Ip+"端口号为:"+port+"发送的内容为:"+new String(bytes,0,len)); } //Ds.close(); } } package com.oracle.duoxianchengxiawu2; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.util.Scanner; public class UDPSend { public static void main(String[] args) throws IOException { //打包: //明确数据: Scanner scan=new Scanner(System.in); //明确目的地的IP地址: InetAddress inet=InetAddress.getByName("192.168.1.171"); //创建快递公司: DatagramSocket Ds=new DatagramSocket(); while(true){ String Mess=scan.next(); byte[] bytes=Mess.getBytes(); //装包: DatagramPacket Dp=new DatagramPacket(bytes,bytes.length,inet,6666); //发送: Ds.send(Dp); } //释放资源: //Ds.close(); } }
TCP通讯示例代码:
package com.oracle.duoxianchengxiawu3; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; public class TCPClient { public static void main(String[] args) throws IOException { //创建Socket对象连接服务器: Socket soc=new Socket("127.0.0.1",7777); //通过客户端套接字对象Socket对象中的获取字节输出流的方法: OutputStream out=soc.getOutputStream(); //将数据写向服务器: out.write("服务器你好!".getBytes()); //接收服务器端的回复: InputStream in=soc.getInputStream(); byte[] bytes=new byte[1024]; int len=in.read(bytes); System.out.println(new String(bytes,0,len)); //释放资源: soc.close(); //外面的数据和以前一样,但是服务器或者客户端发送的数据用Socket里面获得! } } package com.oracle.duoxianchengxiawu3; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; public class TCPServer { public static void main(String[] args) throws IOException { //创建服务器套接字: ServerSocket server=new ServerSocket(7777); //调用accept方法与客户端创建连接: Socket soc=server.accept(); InputStream insoc=soc.getInputStream(); byte[] bytes=new byte[1024]; int len=insoc.read(bytes); System.out.println(new String(bytes,0,len)); //服务器给客户端回复: OutputStream out=soc.getOutputStream(); out.write("收到!".getBytes()); server.close(); } }