在jdk1.5之后引入了了线程池的概念
生产线程池的工厂类
/*
线程池:JDK1.5之后提供的
java.util.concurrent.Executors:线程池的工厂类,用来生成线程池
Executors类中的静态方法:
static ExecutorService newFixedThreadPool(int nThreads) 创建一个可重用固定线程数的线程池
参数:
int nThreads:创建线程池中包含的线程数量
返回值:
ExecutorService接口,返回的是ExecutorService接口的实现类对象,我们可以使用ExecutorService接口接收(面向接口编程)
java.util.concurrent.ExecutorService:线程池接口
用来从线程池中获取线程,调用start方法,执行线程任务
submit(Runnable task) 提交一个 Runnable 任务用于执行
关闭/销毁线程池的方法
void shutdown()
线程池的使用步骤:
1.使用线程池的工厂类Executors里边提供的静态方法newFixedThreadPool生产一个指定线程数量的线程池
2.创建一个类,实现Runnable接口,重写run方法,设置线程任务
3.调用ExecutorService中的方法submit,传递线程任务(实现类),开启线程,执行run方法
4.调用ExecutorService中的方法shutdown销毁线程池(不建议执行)
*/
代码实现
public class Demo01ThreadPool {
public static void main(String[] args) {
//1.使用线程池的工厂类Executors里边提供的静态方法newFixedThreadPool生产一个指定线程数量的线程池
ExecutorService es = Executors.newFixedThreadPool(2);
//3.调用ExecutorService中的方法submit,传递线程任务(实现类),开启线程,执行run方法
es.submit(new RunnableImpl());//pool-1-thread-1创建了一个新的线程执行
//线程池会一直开启,使用完了线程,会自动把线程归还给线程池,线程可以继续使用
es.submit(new RunnableImpl());//pool-1-thread-1创建了一个新的线程执行
es.submit(new RunnableImpl());//pool-1-thread-2创建了一个新的线程执行
//4.调用ExecutorService中的方法shutdown销毁线程池(不建议执行)
es.shutdown();
es.submit(new RunnableImpl());//抛异常,线程池都没有了,就不能获取线程了
}
}
/*
2.创建一个类,实现Runnable接口,重写run方法,设置线程任务
*/
public class RunnableImpl implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"创建了一个新的线程执行");
}
}
网络编程
网络编程前文:
import java.net.InetAddress;
import java.net.UnknownHostException;
//static InetAddress getByName(String host)
// 确定主机名称的IP地址。主机名称可以是机器名称,也可以是IP地址
//String getHostName() 获取此IP地址的主机名
//String getHostAddress() 返回文本显示中的IP地址字符串
public class InetadressDemo1 {
public static void main(String[] args) throws UnknownHostException {
InetAddress address = InetAddress.getByName("沉迷代码");
String hostName = address.getHostName();
System.out.println("主机名为" + hostName);
String ip = address.getHostAddress();
System.out.println("IP为" + ip);
}
}
一个端口号只能被一个应用程序使用
端口号是由两个字节组成 取值在0—65535之间,但是1024之前的端口号不能用,已经被系统分配给已知的网络软件了,网络软件的端口号不能重复
通信概述
UDP编程
Client端代码实现
import java.io.IOException;
import java.net.*;
public class ClientDemo {
public static void main(String[] args) throws IOException {
//1.找码头
DatagramSocket ds = new DatagramSocket();//空参表示会随机绑定一个动态端口把包发送出去
//2.打包
//DatagramPacket(byte[] buf, int length, InetAddress address, int port)
String s = "这是我要发送的数据";
//把一个字符串变成字节数组
byte[] bytes = s.getBytes();
InetAddress address = InetAddress.getByName("127.0.0.1");
int port = 10000;
DatagramPacket dp = new DatagramPacket(bytes,bytes.length,address,port);
//3.由码头发送包裹
ds.send(dp);
//4.关闭资源
ds.close();
}
}
Server端代码实现
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class ServerDemo {
//注意点:
//1.要先运行接收端,再运行发送端;因为UDP是面向无连接的,
// 所以发送端直接把数据已经发出来了 在运行接收端就收不到数据了。。
//2.如果接收端再启动之后,没有接收到数据,那么会死等(阻塞).
//3.在接收数据的时候,需要调用一个getLength方法,表示接收到了多少字节
public static void main(String[] args) throws IOException {
//1.找码头 ---- 表示接收端从10000端口接收数据的.
DatagramSocket ds = new DatagramSocket(10000);
//2,创建一个新的箱子
byte [] bytes = new byte[1024];
DatagramPacket dp = new DatagramPacket(bytes,bytes.length);
//3.接收礼物,把礼物放到新的箱子中
System.out.println("-----------接收前----------");
ds.receive(dp);
System.out.println("------------接收后---------");
//4.从新的箱子里面获取礼物
// byte[] data = dp.getData();
//因为字节数组定义1024会输出很多空格,所以在这里把有效数据转换成字符串
int length = dp.getLength();
//把数组变成字符串打印输出
System.out.println(new String(bytes,0,length));
//5.关掉资源
ds.close();
}
}
UDP通讯练习题
发送端代码实现
import java.io.IOException;
import java.net.*;
import java.util.Scanner;
public class ClientDemo {
public static void main(String[] args) throws IOException {
Scanner sc = new Scanner(System.in);
DatagramSocket ds = new DatagramSocket();
while (true) {//选中后ctrl+alt+t即可
//传输的数据就来自键盘录入
String s = sc.nextLine();
if("886".equals(s)){
break;
}
//要把字符串转换为字节数组才能传输
byte[] bytes = s.getBytes();
//告知要把数据发送到哪台电脑上
InetAddress address = InetAddress.getByName("127.0.0.1");
int port = 10000;
DatagramPacket dp = new DatagramPacket(bytes,bytes.length,address,port);
ds.send(dp);
}
ds.close();
}
}
接收端代码实现
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
public class ServerDemo {
public static void main(String[] args) throws IOException {
DatagramSocket ds = new DatagramSocket(10000);
while (true) {
byte [] bytes = new byte[1024];
DatagramPacket dp = new DatagramPacket(bytes,bytes.length);
ds.receive(dp);
//把数据放到数组中
byte[] data = dp.getData();
//然后设置有效字符 把字符转换成字符串输出
int length = dp.getLength();
System.out.println(new String(data,0,length));
}
// ds.close();
}
}
UDP拓展——实现聊天功能
实现客户机和服务器之间互动,一发一回;这种缺陷是只能一发一回,因为接收端的receive方法是个阻塞的,它是需要等客户端发过来消息才进行下一步
先做一个工具类,代码实现
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* @Author: kerwin
* @Description:
*/
public class NetworkUtils {
// 获取发送端数据报包的方法
public static DatagramPacket getSendPacket(String msg, String ip, int port) throws UnknownHostException {
// 把需要发送的数据转化为字节数组
byte[] bytes = msg.getBytes();
InetAddress targetIp = InetAddress.getByName(ip);
// 创建用于发送的数据报包
DatagramPacket sendPacket =
new DatagramPacket(bytes, 0, bytes.length, targetIp, port);
return sendPacket;
}
// 获取用于接收的数据报包的方法
public static DatagramPacket getReceivePacket() {
byte[] bytes = new byte[1024];
DatagramPacket receivePacket = new DatagramPacket(bytes, 0, bytes.length);
return receivePacket;
}
// 解析数据报包的方法
public static String parsePacket(DatagramPacket packet){
byte[] data = packet.getData();
int offset = packet.getOffset();
int length = packet.getLength();
String s = new String(data, offset, length);
return s;
}
}
发送端代码实现
import com.kerwin.lianxi.util.NetworkUtils;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketAddress;
/**
* @Author: kerwin
* @Description:
*/
public class Sender {
public static void main(String[] args) throws IOException {
// 创建用于发送的socket对象
DatagramSocket datagramSocket = new DatagramSocket(8888);
// 键盘接收数据
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String msg;
while ((msg = br.readLine()) != null) {
// 把键盘输入的数据封装到数据报包里面
DatagramPacket sendPacket = NetworkUtils.getSendPacket(msg, "127.0.0.1", 9999);
// send方法发送出去
datagramSocket.send(sendPacket);
// 以上是发送的接收
// 下面是接收数据
// 获取一个用于接收的数据报包
DatagramPacket receivePacket = NetworkUtils.getReceivePacket();
// receive方法接收
// receive方法是一个阻塞方法
datagramSocket.receive(receivePacket);
// 解析数据报包
String s = NetworkUtils.parsePacket(receivePacket);
// 获取数据 报包的ip地址
SocketAddress socketAddress = receivePacket.getSocketAddress();
System.out.println("接收到了来自"+socketAddress+"的消息: " + s);
}
}
}
接收端代码实现
import com.kerwin.lianxi.util.NetworkUtils;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketAddress;
import java.util.Scanner;
/**
* @Author: kerwin
* @Description:
*/
public class Receiver {
public static void main(String[] args) throws IOException {
// 创建用于接收的socket对象
DatagramSocket datagramSocket = new DatagramSocket(9999);
// 接盘接收数据
Scanner scanner = new Scanner(System.in);
while (true) {
// 创建用于接收的数据报包
DatagramPacket receivePacket = NetworkUtils.getReceivePacket();
// receive
datagramSocket.receive(receivePacket);
// 解析数据报包
String msg = NetworkUtils.parsePacket(receivePacket);
SocketAddress socketAddress = receivePacket.getSocketAddress();
System.out.println("Receiver接收到了来自"+socketAddress+"的消息: " + msg);
// 下面是给Sender发送消息
// 创建用于发送的数据报包 数据来源就是键盘输入
String s = scanner.nextLine();
DatagramPacket sendPacket = NetworkUtils.getSendPacket(s, "127.0.0.1", 8888);
// send方法发送出去
datagramSocket.send(sendPacket);
}
}
}
UDP拓展——把上面的案例优化
实现可以重复发送消息,这里使用多线程的方式,一个线程是发送,一个线程接收;还需要两个类,一个人和另一个人
两个类代码实现
import java.io.IOException;
import java.net.DatagramSocket;
/**
* @Author: kerwin
* @Description:
*/
public class OnePerson {
public static void main(String[] args) throws IOException {
// 创建UDP的socket对象
DatagramSocket datagramSocket = new DatagramSocket(8888);
// 创建线程并启动
new Thread(new SendTask(datagramSocket,"127.0.0.1",9999)).start();
new Thread(new ReceiveTask(datagramSocket)).start();
}
}
import java.io.IOException;
import java.net.DatagramSocket;
/**
* @Author: kerwin
* @Description:
*/
public class AnotherPerson {
public static void main(String[] args) throws IOException {
// 创建UDP的socket对象
DatagramSocket datagramSocket = new DatagramSocket(9999);
// 创建线程并启动
new Thread(new SendTask(datagramSocket,"127.0.0.1",8888)).start();
new Thread(new ReceiveTask(datagramSocket)).start();
}
}
两个线程代码实现
import com.kerwin.lianxi.util.NetworkUtils;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.UnknownHostException;
import java.util.Scanner;
/**
* @Author: kerwin
* @Description:
*/
public class SendTask implements Runnable{
// 定义成员变量
DatagramSocket datagramSocket;
// 目标ip port
String ip;
int port;
public SendTask(DatagramSocket datagramSocket, String ip, int port) {
this.datagramSocket = datagramSocket;
this.ip = ip;
this.port = port;
}
@Override
public void run() {
// 只干一件事情 发送消息
Scanner scanner = new Scanner(System.in);
while (true) {
// 从键盘接收数据
String sendMsg = scanner.nextLine();
// 创建用于发送的数据报包
try {
DatagramPacket sendPacket = NetworkUtils.getSendPacket(sendMsg, ip, port);
// 发送出去
datagramSocket.send(sendPacket);
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
import com.kerwin.lianxi.util.NetworkUtils;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/**
* @Author: kerwin
* @Description:
*/
public class ReceiveTask implements Runnable{
// 成员变量
DatagramSocket datagramSocket;
public ReceiveTask(DatagramSocket datagramSocket) {
this.datagramSocket = datagramSocket;
}
@Override
public void run() {
// 只干一件事情 接收数据 并打印
while (true) {
// 创建接收的数据报包
DatagramPacket receivePacket = NetworkUtils.getReceivePacket();
// receive方法接收数据
try {
datagramSocket.receive(receivePacket);
// parse数据报包
String receiveMsg = NetworkUtils.parsePacket(receivePacket);
System.out.println(receiveMsg);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
TCP编程
客户端代码实现
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
/*
TCP通信的客户端:向服务器发送连接请求,给服务器发送数据,读取服务器回写的数据
表示客户端的类:
java.net.Socket:此类实现客户端套接字(也可以就叫“套接字”)。套接字是两台机器间通信的端点。
套接字:包含了IP地址和端口号的网络单位
构造方法:
Socket(String host, int port) 创建一个流套接字并将其连接到指定主机上的指定端口号。
参数:
String host:服务器主机的名称/服务器的IP地址
int port:服务器的端口号
成员方法:
OutputStream getOutputStream() 返回此套接字的输出流。
InputStream getInputStream() 返回此套接字的输入流。
void close() 关闭此套接字。
实现步骤:
1.创建一个客户端对象Socket,构造方法绑定服务器的IP地址和端口号
2.使用Socket对象中的方法getOutputStream()获取网络字节输出流OutputStream对象
3.使用网络字节输出流OutputStream对象中的方法write,给服务器发送数据
4.使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象
5.使用网络字节输入流InputStream对象中的方法read,读取服务器回写的数据
6.释放资源(Socket)
注意:
1.客户端和服务器端进行交互,必须使用Socket中提供的网络流,不能使用自己创建的流对象
2.当我们创建客户端对象Socket的时候,就会去请求服务器和服务器经过3次握手建立连接通路
这时如果服务器没有启动,那么就会抛出异常ConnectException: Connection refused: connect
如果服务器已经启动,那么就可以进行交互了
*/
public class TCPClient {
public static void main(String[] args) throws IOException {
//1.创建一个客户端对象Socket,构造方法绑定服务器的IP地址和端口号
Socket socket = new Socket("127.0.0.1",8888);
//2.使用Socket对象中的方法getOutputStream()获取网络字节输出流OutputStream对象
OutputStream os = socket.getOutputStream();
//3.使用网络字节输出流OutputStream对象中的方法write,给服务器发送数据
os.write("你好服务器".getBytes());
//4.使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象
InputStream is = socket.getInputStream();
//5.使用网络字节输入流InputStream对象中的方法read,读取服务器回写的数据
byte[] bytes = new byte[1024];
int len = is.read(bytes);
System.out.println(new String(bytes,0,len));
//6.释放资源(Socket)
socket.close();
}
}
服务器代码实现
未完持续...