线程的概念

线程是指在一个进程中执行的独立的、可调度的执行单元。在JAVA中,线程是Thread类的实例,可以通过继承Thread类或实现Runnable接口来创建线程。每个线程都有自己的执行路径和执行状态,可以共享进程的资源,例如内存、文件句柄等,因此线程之间的通信和协作比进程之间更加高效。

线程的结构

在JAVA中,线程的结构通常包括ID、程序计数器、寄存器集合、堆栈和状态等。线程ID是一个唯一标识符,用于区分不同的线程。程序计数器用于当前线程的执行的位置,寄存器集合用于保存线程的上下文信息,堆栈用于保存线程的局部变量和方法用栈,状态用于记录线程的执行状态,例如就行、运行、阻塞等。

线程的创建和启动

在JAVA中,线程的创建和启动可以通过继承Thread类或实现Runnable接口来实现。例如,可以创建一个继承Thread类的子类,并重写run()方法来定义线程的执行逻辑,让后通过start()方法来启动线程。也可以创建一个实现Runnable接口的类,并实现run()方法来定义线程的执行逻辑,然后通过创建Thread对象并传入Runnable对象来启动线程。

线程的同步和协作

在JAVA中,线程的并发执行可能会导致一些问题,例如竞态条件、死锁、饥饿等。为了避免这些问题,需要使用同步机制,例如锁、信号量、条件变量等,来保证线程之间的互斥和协作。

总结

线程的优点:

1、提高系统吞吐率 2、提高IO效率 3、充分利用多核资源

线程的缺点:

1、安全问题 2、资源竞争 3、上下文切换 4、可靠性

继承Thread类创建线程

在Java中,可以通过继承Thread类来创建线程。例如,可以创建一个继承Thread类的子类,并重写run()方法来定义线程的执行逻辑,然后通过调start()方法来启动线程。

线程的生命周期

在Java中,线程的生命周期通常包括新建、就绪、运行、阻塞和终止等状态。新建状态是指线程对象被创建但还没启动的状态;就绪状态是指线程已经准备好执行但还没获得CPU资源的状态;阻塞状态是指线程因为某些原因暂时停止执行的状态;终止状态是指线程完毕或者因为异常等原因被终止的状态。

线程的优先级

在Java中,每个线程都有一个优先级,用于指定线程在竞争CPU资源时的优先级。线程的优先级可以通过setPriority()方法来设置,取值范围为1~10,其中1为最低优先级,10为最高优先级。线程的优先级并不是绝对的,只是一个相对的概念,具体的调度顺序还受到操作系统和CPU的影响。

总结

继承Thread类是Java中多线程编程的一种常见方式,它可以提高程序的性能和响应速度,但也需要注意线程之间同步和协作,以避免出现问题。了解Java中继承Thread类的多线程编程,可以帮助我们更好地理解并发编程的本质,提高程序的可靠性性能。同时,了解线程的优先级、异常处理、停止和调试等方面的知识,可以帮助我们更好地掌握线程编程的技巧和方法。

Main

public class Main {
public static void main(String[] args)
{
TestThread01 testThread01_1=new TestThread01(1,4,"张三");
TestThread01 testThraad01_2=new TestThread01(1,4,"李四");
testThread01_1.start();
testThraad01_2.start();
System.out.println("main Thread finished");
TestThread02 testThread02_1=new TestThread02(3,4,"王五");
System.out.println("线程状态:"+testThread02_1.getState());
testThread02_1.start();
System.out.println("线程状态:"+testThread02_1.getState());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
try {
testThread02_1.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("线程状态:"+testThread02_1.getState());
System.out.println("main Thread finished");
TestThread02 testThread02_2=new TestThread02(3,4,"王五");
testThread02_2.setPriority(6);
testThread02_2.start();
TestThread03 testThread03 = new TestThread03();
testThread03.setDaemon(true);
testThread03.start();
System.out.println("main Thread finished");
}
}

TestThread01

private int id;
private int loopCount;
public TestThread01(int pId,int pLoopCount,String pName)
{
this.id=pId;
this.loopCount=pLoopCount;
this.setName(pName);
}
@Override
public void run()
{
for(int i=0;i<loopCount;i++)
{
System.out.println("Thread Name:"+Thread.currentThread().getName()+", loop:"+i);
}
}

TestThread02

public class TestThread02 extends Thread
{
private int id;
private int loopCount;
public TestThread02(int pId,int pLoopCount,String pName)
{
this.id=pId;
this.loopCount=pLoopCount;
this.setName(pName);
}
@Override
public void run()
{
for(int i=0;i<loopCount;i++)
{
System.out.println("Thread Name:"+Thread.currentThread().getName()+", loop:"+i);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}

TestThread03

public class TestThread03 extends Thread
{
@Override
public void run()
{
System.out.println("我是守护线程吗?"+this.isDaemon());
while(true)
{
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}

实现Runnable接口

Runnable接口简介

1、Runnable接口位于java.lang包中

2、Runnable接口描述了一个可以作为线程运行的类

3、只包含一个方法:void run()

为什么要使用Runnable接口

1、适用于多线程编程

2、实现Runnable接口的类可以被多个线程执行

3、避免Java单继承限制

如何实现Runnable接口

1、创建一个类,实现Runnable接口

2、重写run()方法,定义线程要执行的任务

3、创建Thread对象,将Runnable实现类的实例作为参数传递

4、调用Thread对象的start()方法启动线程

Runnable接口与Thread类的关系

Runnable接口通常与Thread类一起使用

Thread类实现了Runnable接口

Thread类提供了更多线程管理功能,如设置优先级、线程状态查询等

总结

Runnable接口用于避免单继承局限性

实现Runnable接口的类更方便

与Thread类结合使用,实现灵活的线程管理

public class Main {
public static void main(String[] args)
{
MyRunnable myRunnable = new MyRunnable();
myRunnable.run();
Thread thread01 = new Thread(myRunnable,"thread01");
thread01.start();
Thread thread02 = new Thread(myRunnable,"thread02");
thread01.start();
}
}

线程启动

使用Thread类创建线程

实现Runnable接口或者继承Thread类

调用start()方法启动线程

线程调度

线程调度由操作系统管理

Java提供了线程优先级设置

优先级范围:1(最低)到10(最高)

线程挂起

使用Thread.sleep方法让线程暂停

使用TimeUnit类让线程暂停

使用LockSupport让线程变相暂停(注意:先解除休眠再休眠不会进入死循环)

import java.util.concurrent.locks.LockSupport;

public class Main {
public static void main(String[] args)
{
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
MyThread t3 = new MyThread();
t1.setName("a");
t2.setName("b");
t3.setName("c");
t1.setPriority(1);
t2.setPriority(5);
t3.setPriority(10);
t1.start();
t2.start();
t3.start();
MyThreadSleep myThreadSleep = new MyThreadSleep();
myThreadSleep.start();
MyThread t4 = new MyThread();
t4.start();

try {
Thread.sleep(4000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
LockSupport.unpark(t4);
MyThread t5 = new MyThread();
t5.start();
LockSupport.unpark(t5);
}
}

总结

线程调度:操作系统管理,Java提供优先级设置

线程挂起:说了3种

线程间的通信

Object:wait()、notify()和notifyAll方法

wait()来自Object

sleep不会释放锁,需要捕捉异常;wait会释放锁

使用synchronized和volatile关键字

volatile修饰变量,多个角色操作同一个变量

synchronized修饰代码段,可以是块,也可以是方法

Condition: await()、signal()和signalAll()方法

使用方法跟wait雷同,升级版

优势1:多个condition条件

优势2:不担心唤醒和等待的顺序问题

Main

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Main {
public static void testWait()
{
Supermarket sm= new Supermarket();

Thread[] producers = new Thread[1];

for(int i=0;i< producers.length;i++)
{
producers[i] = new Thread(new Producter(sm),new String(i+"号生产者"));
producers[i].start();
}

Thread[] consumers = new Thread[3];
for(int i=0;i< consumers.length;i++)
{
consumers[i] = new Thread(new Consumer(sm),new String(i+"号消费者"));
consumers[i].start();
}
}
public static void testCondition()
{
final Lock lock = new ReentrantLock();
final Condition condition01 = lock.newCondition();
final Condition condition02 = lock.newCondition();
final Condition condition03 = lock.newCondition();

new Thread(()->{
System.out.println("线程执行第一步");
lock.lock();
try
{
condition01.await();
System.out.println("condition01 wake");
Thread.sleep(3000);
condition02.signal();
condition03.await();
System.out.println("condition03 wake");
}catch (InterruptedException e)
{
e.printStackTrace();
}finally
{
lock.unlock();
}
}).start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
lock.lock();
try
{
condition01.signal();
condition02.await();
System.out.println("condition02 wake");
Thread.sleep(5000);
condition03.signal();

} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}

}
public static void main(String[] args)
{
//testWait();
testCondition();
}
}

Supermarket

public class Supermarket
{
private int curGoodCount = 0;
private int maxGoodCount = 10;
public synchronized void add()
{
if(curGoodCount<maxGoodCount)
{
curGoodCount++;
System.out.println(Thread.currentThread().getName()+"在加紧生产,货架现有"+curGoodCount+"个商品");
notify();
}
else
{
try {
wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
public synchronized void sub()
{
if(curGoodCount>0)
{
curGoodCount--;
System.out.println(Thread.currentThread().getName()+"又来购物了,还剩"+curGoodCount+"还剩"+curGoodCount+"个商品");
notify();
}
else
{
try {
wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}

}

}

Producer

public class Producter implements Runnable
{
Supermarket supermarket;
public Producter(Supermarket su)
{
supermarket = su;
}

@Override
public void run()
{
while(true)
{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
supermarket.add();
}

}
}

Consumer

public class Consumer implements Runnable
{
Supermarket supermarket;
public Consumer(Supermarket su)
{
supermarket = su;
}
@Override
public void run()
{
while(true)
{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
supermarket.sub();
}

}
}
public class Consumer implements Runnable
{
Supermarket supermarket;
public Consumer(Supermarket su)
{
supermarket = su;
}
@Override
public void run()
{
while(true)
{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
supermarket.sub();
}

}
}

InetAddress类概述

用途:InetAddress类用于网络编程,它提供了IP地址和主机名之间的映射。

特点:InetAddress类没有公共构造函数,我们只能通过类的方法来创建其对象。

InetAddress类的使用

构造:getByAddress(String host):此方法接受一个主机名作为参数,返回该主机名的InetAddress对象。

使用:getHostAddress():此方法返回Inetddress对象的主机名。如果对象是通过IP地址创建的,那么它将返回IP地址作为主机名。

getLocalHost():此方法返回一个InetAddress对象,该对象表示本地主机。它是获取本地主机名和IP地址的一种快捷方式。

其它:

配合NetworkInterface使用

看不懂、、、对计算机 网络没有概念

import java.net.*;
import java.util.Enumeration;
import java.util.List;

public class Main {
public static String macBytes2String(byte[] buf)
{
if(buf==null)
{
return null;
}
StringBuffer sb = new StringBuffer();
for(int i=0;i<buf.length;i++)
{
if(i!=0)
{
sb.append("-");
}
String tmp = Integer.toHexString(buf[i]&0xFF);
if(tmp.length()==1)
{
sb.append("0");
}
sb.append(tmp);
}
return sb.toString().toUpperCase();
}


public static void main(String[] args) throws UnknownHostException, SocketException {
//InetAddress
InetAddress inet01 = InetAddress.getByName("www.baidu.com");//根据域名获取对应的InetAddress对象
System.out.println("Host Name:" + inet01.getHostName());//获取解析后的主机名
System.out.println("IP Address:" + inet01.getHostAddress());//获取解析后的IP地址


InetAddress inet02 = InetAddress.getByName("36.155.132.76");
System.out.println("Host Name:" + inet02.getHostName());
System.out.println("IP Address:" + inet02.getHostAddress());

InetAddress inet03 = InetAddress.getLocalHost();//获取本地主机的InetAddress对象
System.out.println("Host Name:" + inet03.getHostName());//获取本地主机的主机名
System.out.println("IP Address:" + inet03.getHostAddress());//获取本地主机的IP地址
Enumeration<NetworkInterface> networkInerfaces = NetworkInterface.getNetworkInterfaces();//获取当前主机上所有网络接口的枚举
//外层的 while 循环遍历每个网络接口,内层的 while 循环遍历每个接口的InetAddress对象。
while(networkInerfaces.hasMoreElements())
{
NetworkInterface nif = networkInerfaces.nextElement();
Enumeration<InetAddress>inetAddresses=nif.getInetAddresses();//获取当前网络接口的所有接口地址
while(inetAddresses.hasMoreElements())
{
InetAddress addr = inetAddresses.nextElement();
List<InterfaceAddress> interfaceAddress=nif.getInterfaceAddresses();
System.out.println(interfaceAddress);
System.out.println(nif.getDisplayName());//获取网络接口的显示名称
System.out.println(nif.getName());//获取网络接口的名称
System.out.println(addr.getHostName());//获取InetAddress对象的主机名
System.out.println(addr.getAddress());//获取InetAddress对象的原始IP地址
byte[] mac = nif.getHardwareAddress();//获取网络接口的物理地址(MAC地址
System.out.println(macBytes2String(mac));
System.out.println();
}
}
}
}

URL的概念

URL(统一资源定位器)是互联网上用于秒速信息位置的一种方式。

他是互联网上标准资源的地址。互联网上的每个文件都有一个唯一的URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它。

结构:https://blog.csdn.net/xinzi123/arcle/detail8?w=445&id=5

协议://域名:端口/目录/目录?参数1=参数1值&参数2=参数2值

URL的创建

在Java中,我们可以使用Java.net.URL类来创建URL对象

同步请求

同步请求是指客户端发出请求后,必须等待服务器回应才能进行下一步操作。

在此期间,客户端不能做其他操作,这种方式的好处是编程模型简单,缺点是等待服务器响应的时间可能会很长

总结

URL是互联网上的资源地址,java提供了URL类来操作URL

import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Main {

public static void get()
{
try {
URL url = new URL("https://www.baidu.com");
HttpURLConnection conn=(HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Accept-Charset","UTF-8");
conn.setRequestProperty("Accept","text/html");
conn.connect();
int responseCode = conn.getResponseCode();
if(responseCode==HttpURLConnection.HTTP_OK)
{
InputStream inputStream=conn.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream,"UTF-8");
Stream<String> stringStream = new BufferedReader(inputStreamReader).lines();
String streamToString = stringStream.collect(Collectors.joining());
System.out.println(streamToString);
}
else
{
System.out.println("error Code:"+responseCode);
}

} catch (MalformedURLException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static void post()
{
try {
URL url = new URL("https://www.baidu.com");
HttpURLConnection conn=(HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Accept-Charset","UTF-8");
conn.setRequestProperty("Accept","text/html");
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setUseCaches(false);
conn.connect();
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(conn.getOutputStream()));
writer.write("ddfsadfasdf");
writer.close();


int responseCode = conn.getResponseCode();
if(responseCode==HttpURLConnection.HTTP_OK)
{
InputStream inputStream=conn.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream,"UTF-8");
Stream<String> stringStream = new BufferedReader(inputStreamReader).lines();
String streamToString = stringStream.collect(Collectors.joining());
System.out.println(streamToString);
}
else
{
System.out.println("error Code:"+responseCode);
}

} catch (MalformedURLException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static void downloadFile()
{
try {
URL url = new URL("https://www.baidu.com");
HttpURLConnection conn=(HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Accept-Charset","UTF-8");
conn.setRequestProperty("Accept","text/html");
conn.connect();

int responseCode = conn.getResponseCode();
if(responseCode==HttpURLConnection.HTTP_OK)
{
InputStream inputStream=conn.getInputStream();
File dir = new File("downloadFileDir");
if(!dir.exists())
{
dir.mkdirs();
}
File file = new File(dir,"fname.html");
FileOutputStream fos = new FileOutputStream(file);
byte[] buffer = new byte[1024];
int len=-1;
while((len=inputStream.read(buffer))!=-1)
{
fos.write(buffer,0,len);
}
fos.close();
}
else
{
System.out.println("error Code:"+responseCode);
}

} catch (MalformedURLException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args)
{
//同步get请求
//get();
//同步post请求
//post();
//下载文件
downloadFile();
}
}

异步请求

Okhttp3的优点

提供了对HTTP/2和SPDY的支持,这使得对同一个主机发出的所有请求都可以共享相同的套接字连接

如果HTTP/2和SPDY不可用,OkHttp会使用连接池来复用连接以提高效率

提供了对GZIP的默认支持来降低传输内容的大小

提供了对HTTP响应的缓存机制,可以避免不必要的网络请求

当网络出现问题是,Okhttp会自动重试一个主机的多个IP地址

Okhttp3使用教程

GRADLE引入包

创建OkHttpClient实例

简单来说,通过OkHttpClient可以发送一个Http请求,并读取该Http请求的响应,它是一个生产Call的工厂。

此外,收益于一个共享的响应缓存/线程池/复用的连接等因素,绝大多数应用使用一个OkHttpClient实例,便可以满足整个应用的Http请求

GET和POST

HTTP头部的设置和读取

HTTP头的数据结构是Map<String,List>类型。也就是说,对于每个HTTP头,可能有多个值。但是大部分HTTP头都只有一个值,只有少部分HTTP头允许多个值

import okhttp3.*;
import org.jetbrains.annotations.NotNull;

import javax.net.ssl.*;
import java.io.IOException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Map;
import java.util.concurrent.TimeUnit;
public class MyNet
{
private OkHttpClient okHttpClient;
public MyNet()
{
// 创建一个 OkHttpClient.Builder 实例,用于配置 OkHttpClient
OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
clientBuilder.readTimeout(20, TimeUnit.SECONDS);
clientBuilder.connectTimeout(5,TimeUnit.SECONDS);
clientBuilder.writeTimeout(60,TimeUnit.SECONDS);

// 设置自定义的 SSL 套接字工厂
clientBuilder.sslSocketFactory(mySslSocketFactory());
// 设置自定义的主机名验证器,这里简单返回 true,表示不进行主机名验证
clientBuilder.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String s, SSLSession sslSession) {
return true;
}
});
// 构建 OkHttpClient 实例
okHttpClient = clientBuilder.build();
}
// 定义一个内部类 TrustAllCerts,实现 X509TrustManager 接口
class TrustAllCerts implements X509TrustManager
{
// 空实现,表示信任所有客户端证书
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException
{


}
// 空实现,表示信任所有客户端证书
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException
{


}
// 返回空的证书数组,表示不接受任何证书颁发机构的证书
@Override
public X509Certificate[] getAcceptedIssuers()
{
return new X509Certificate[0];
}
}
private SSLSocketFactory mySslSocketFactory()
{
SSLSocketFactory sslSocketFactory = null;
try
{
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null,new TrustManager[]{new TrustAllCerts()},new SecureRandom());
sslSocketFactory = sslContext.getSocketFactory();

}catch (Exception ee)
{
ee.printStackTrace();
}
return sslSocketFactory;
}
public interface MyCallBack
{
void success(Call call,Response response) throws IOException;
void failed(Call call ,IOException e);
}
public void get(String url,MyCallBack myCallBack)
{
Request.Builder builder = new Request.Builder();
Request request = builder.get().url(url).build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e)
{
myCallBack.failed(call,e);

}

@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException
{
myCallBack.success(call,response);

}
});
}
public void post(String url, Map<String,String> bodyMap, MyCallBack myCallBack)
{

}
}

Socket通信

Socket通信基本步骤

创建Socket

绑定Socket到地址和端口

监听连接

接受连接

发送和接受数据

关闭Socket

Main

import java.io.InputStream;
import java.io.OutputStream;
import java.net.DatagramSocket;
import java.net.ServerSocket;
import java.net.Socket;

public class Main {
public static void main(String[] args) {

if (false){
try{
// DatagramSocket datagramSocket = DatagramSocket(2343);
ServerSocket serverSocket = new ServerSocket(7890);
System.out.println("服务器已启动。");

Socket socket = serverSocket.accept();
System.out.println("客户端已经连接:" + socket.getInetAddress().getHostAddress());

InputStream inputStream = socket.getInputStream();
OutputStream outputStream = socket.getOutputStream();

//接收一次数据
byte[] buffer = new byte[1024];
while (true){
int bytesRead = inputStream.read(buffer);
String requestData = new String(buffer, 0, bytesRead);
System.out.println("接收到客户端发送的数据:" + requestData);
//根据不同数据,不同格式,走不同的数据处理分支
if (requestData.equals("exit")){
break;
}
}


//发送一次数据
String responseData = "hello client!";
outputStream.write(responseData.getBytes());
System.out.println("向客户端发送响应数据:" + responseData);


socket.close();


}catch (Exception ee){
ee.printStackTrace();
}
}



ServerSocket serverSocket = null;
Socket socket = null;
try{
// DatagramSocket datagramSocket = DatagramSocket(2343);
serverSocket = new ServerSocket(7890);
System.out.println("服务器已启动。");

while (true){
socket = serverSocket.accept();
System.out.println("客户端已经连接:" + socket.getRemoteSocketAddress() + ", 端口:" + socket.getPort());

new Thread(new Service(socket)).start();
}


}catch (Exception ee){
ee.printStackTrace();
}
}
}

Clientv1

import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class Clientv1 {
public static void main(String[] args) {
try{
Socket socket = new Socket("192.168.31.106", 7890);

InputStream inputStream = socket.getInputStream();
OutputStream outputStream = socket.getOutputStream();

//数据源可以是命令行窗口的用户输入,也可以是一个文件里面的内容,也可以是其他数据
String send1 = "hello server";
outputStream.write(send1.getBytes());
System.out.println("向服务器发送数据:" + send1);

String send2 = "exit";
outputStream.write(send2.getBytes());
System.out.println("向服务器发送数据:" + send2);

byte[] buff = new byte[1024];
int bytesRead = inputStream.read(buff);
String recv = new String(buff, 0, bytesRead);
System.out.println("接收到服务器返回数据:" + recv);

socket.close();

}catch (Exception ee){
ee.printStackTrace();
}


}
}

Service

import java.io.*;
import java.net.Socket;

public class Service implements Runnable{
private InputStream input = null;
private OutputStream output = null;
private Socket s = null;

private ObjectOutputStream oos= null;
private PrintWriter pw = null;

public Service(Socket s) {
this.s = s;
}


@Override
public void run() {
try {
input = s.getInputStream();
output = s.getOutputStream();

oos = new ObjectOutputStream(output);
oos.writeInt(1314);
oos.writeDouble(13.14);
Student st = new Student("张三", 567);
oos.writeObject(st);
oos.flush();


} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

Student

public class Student {
private String sName;
private int sId;
public Student(String sName, int sId) {
this.sName = sName;
this.sId = sId;
}
@Override
public String toString() {
return "Student{" +
"sName='" + sName + '\'' +
", sId=" + sId +
'}';
}
}