学习目标:
单线程与多线程,用多线程改造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());
}
}