单线程与多线程,用多线程改造socket服务器,从文件中快速读取思想

本文旨在介绍如何将单线程的Socket服务器改造成多线程,以提高服务性能。通过实现Runnable接口创建线程,并在服务器接收到连接时启动新线程处理对话逻辑。此外,还分享了从文件中快速读取特定数据的设计思想,通过固定长度和规律存储数据,利用RandomAccessFile进行高效定位和读取。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

学习目标:

单线程与多线程,用多线程改造socket服务器,从文件中快速读取思想

学习内容:

一:多线程
概念: 让cpu同时以多个线程执行一个方法run()

步骤:

1、先写一个类实现runnable接口
public class Talk implements Runnable{

public void run(){
	// 想用多个线程同时执行的逻辑
	
}

}
2、然后创建多个线程,放入上面的Talk的对象,然后启动
new Thread(new Talk()).start();
new Thread(new Talk()).start();
new Thread(new Talk()).start();
代码示例:

public class Demo1 implements Runnable {
	private String name;
	
	public Demo1(String name) {
		this.name = name;
	}
	
	public void setName(String name) {
		this.name = name;
	}
	@Override
	public void run() {
		for (int i = 0; i < 6; i++) {
			System.out.println("你好....."+name);
		}
		
	}

}
public class Demo2 implements Runnable {

	@Override
	public void run() {
		for (int i = 0; i < 10; i++) {
			System.out.println("哈喽......."+i);
		}
	}

}
/**
 * 写多线程程序的四部曲
 * 
 * 1、将需要用多线程方式执行的逻辑,写入一个runnable实现类中(run方法中);
 * 2、创建出这个runnable实现类的对象;
 * 3、利用这个runnable对象构造出n个thread线程;
 * 4、将这n个thread启动(thread.start());
 * 
 * 
 * @author ThinkPad
 *
 */
public class DemoTest {

	public static void main(String[] args) {
		 
		Demo1 demo11 = new Demo1("张三");
		 
		Demo1 demo12 = new Demo1("李四");
		
		Demo1 demo13 = new Demo1("王五");
		
		Demo2 demo2 = new Demo2();
		//demo1.run();  // 这样调,只是用单线程普通地执行一下这个run方法而已
		
		// 构造一个线程,指定要执行的逻辑
		Thread thread1 = new Thread(demo11);
		Thread thread2 = new Thread(demo12);
		Thread thread3 = new Thread(demo13);
		Thread thread4 = new Thread(demo2);
		Thread thread5 = new Thread(demo2);
		
		// 这种调用,只是按顺序进行普通的方法调用,是在一个单线程中挨个执行的
		/*thread1.run();
		thread2.run();
		thread3.run();
		thread4.run();
		thread5.run();*/
		
		// 将这5个线程以多线程的方式同时运行
		thread1.start();
		thread2.start();
		thread3.start();
		thread4.start();
		thread5.start();	
	}
}

二:将socketserver改造成多线程

先将对话逻辑写入一个runnable实现类的run方法中
public class Talk implements Runnable{
Socket sc =null;
public Talk(Socket sc){
this.sc = sc;
}
public void run(){
// 对话逻辑
}
}

然后在serversocket程序中:如下改造
while(true){
Socket sc = ss.accept();
new Thread(new Talk(sc)).start()
}
代码示例:

/**
 * 将对话服务器改造成多线程服务
 * @author ThinkPad
 *
 */
 服务器端:
public class ThreadServerDemo {

	public static void main(String[] args) throws Exception {

		ServerSocket ss = new ServerSocket(10011);
		int i=1;
		while (true) {
			
			Socket sc = ss.accept();
			System.out.println("收到连接"+i);
			Talk talk = new Talk(sc);
			new Thread(talk).start();
			i++;
		}
	}
}
/**
 * 用来封装线程中要执行的 对话逻辑(对话流程)
 * @author ThinkPad
 *
 */
public class Talk implements Runnable {
	Socket sc;

	public Talk(Socket sc) {
		this.sc = sc;
	}

	@Override
	public void run() {
		try {
			// 获取输入、输出流
			InputStream in = sc.getInputStream();
			OutputStream out = sc.getOutputStream();
			
			//收第一个问题
			byte[] b = new byte[1024];
			int num = in.read(b);
			System.out.println("收到客户端的问题1:" + new String(b,0,num));
			
			// 回复第一个问题
			out.write("我是宇宙无敌超级美少女战士".getBytes());
			
			// 接收第二个问题
			num = in.read(b);
			System.out.println("收到客户端的问题2:" + new String(b,0,num));
			
			// 回复第二个问题
			out.write("我18岁".getBytes());
			
			
			in.close();
			out.close();
			sc.close();

		} catch (Exception e) {
			System.out.println("发生异常了.......");
		}
	}

}
客户端:
public class ThreadClientDemo {
public static void main(String[] args) throws Exception{
	Socket sc = new Socket("127.0.0.1",10011);
	//发送第一个问题
	OutputStream out = sc.getOutputStream();
	out.write("who are you?".getBytes());
	//接受第一个问题的答案
	InputStream in = sc.getInputStream();
	byte[] b=new byte[10];
	int num = in.read(b);
	System.out.println(new String(b,0,num));
	//发送第二个问题
	out.write("你多大?".getBytes());
	//接受第二个问题的答案
	num = in.read(b);
	System.out.println(new String(b,0,num));
   //关流
	in.close();
	out.close();
	sc.close();
}
}

四、从文件中快速读取想要的数据的设计思想

思想: 先把各个数据字段按照固定长度固定规律存入文件
然后,当需要找id=5这个商品的数据时,就可以根据规律算出这个商品的数据在文件中的起始位置(偏移量)。
用到一个新工具: RandomAccessFile
模板代码:

RandomAccessFile raf = new RandomAccessFile(“d:/p.txt”,“r”);

raf.seek(56);

int id = raf.readInt();

byte[] b = new byte[20];
raf.read(b);
String name = new String(b).trim();
代码实例:

商品实体类:
public class Prodcut {
	private int id;
	private String name;
	private float price;
	
	public Prodcut(int id, String name, float price) {
		this.id = id;
		this.name = name;
		this.price = price;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public float getPrice() {
		return price;
	}

	public void setPrice(float price) {
		this.price = price;
	}
	存数据:
	public class SaveData {
	public static void main(String[] args) throws Exception {
		
		Prodcut p0 = new Prodcut(0, "苹果", 10.5f);
		Prodcut p1 = new Prodcut(1, "泰国榴莲", 16.5f);
		Prodcut p2 = new Prodcut(2, "菠萝", 20.5f);
		Prodcut p3 = new Prodcut(3, "菠萝蜜", 30.5f);
		Prodcut p4 = new Prodcut(4, "香蕉", 19.5f);
		
		save(p0);
		save(p1);
		save(p2);
		save(p3);
		save(p4);
		
	}
	
	public static void save(Prodcut p) throws Exception {
		
		// 将数据按照既定的规则存入文件中
		// id占4个字节,name占20字节,price占4个字节
		DataOutputStream dout = new DataOutputStream(new FileOutputStream("d:/p.txt",true));
		
		dout.writeInt(p.getId()); //写入id
		
		byte[] bytes = p.getName().getBytes("GBK");
		byte[] b = new byte[20];
		// jdk提供的数组拷贝工具方法: 参数1:源数据数组  参数2:从源数组的第几个位置开始拷贝
		// 参数3 :目标数组   参数4:目标数组中放数据的起始位置  参数5:拷贝的长度
		System.arraycopy(bytes, 0, b, 0, bytes.length);
		dout.write(b);  // 将商品名写入文件,占20个字节
		
		dout.writeFloat(p.getPrice());  //将价格按照float数写入文件,占4个字节
		
		dout.close();	
	}
}
取数据方法类:
public class ReadData {
	/**
	 * 根据id查找商品
	 * @param id
	 * @return
	 * @throws Exception
	 */
	public Prodcut findProductById(int id) throws Exception{
		
		// 可以从文件中任何位置开始读数据的工具:RandomAccessFile
		RandomAccessFile raf = new RandomAccessFile("d:/p.dat", "r");
		
		// 读id为2的那个商品的数据
		long pos = id*28;
		
		// 让raf的读取位置跳到指定的pos位置
		raf.seek(pos);
		
		// 然后开始读数据即可
		//  先读4个字节返回一个整数
		int pId = raf.readInt();
		
		// 再读20个字节
		byte[] b = new byte[20];
		int read = raf.read(b);
		// 然后将这20个字节转成字符串,但是尾部有大量空格
		String string = new String(b);
		// 去掉首尾的空格
		String name = string.trim(); 
		
		// 再读价格
		float price = raf.readFloat();
		
		raf.close();
		
		Prodcut prodcut = new Prodcut(pId, name, price);
		return prodcut;
	}
}
测试类:
public class ReadDataTest {
	
	public static void main(String[] args) throws Exception {
		ReadData readData = new ReadData();
		
		Prodcut p = readData.findProductById(3);
		
		System.out.println(p.getId() + ","+ p.getName() +"," + p.getPrice());
		
	}

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值