线程 IO流 网络编程 基础总结

线程

进程---->进行中的程序
线程---->由进程创建 一个进程可以创建多个线程
并发:同一个时刻 多个任务交替执行 造成一种貌似同时进行的错觉 简单来说 单个cpu的多任务就是并发
并行:同一个时刻 多个任务同时执行 多个cpu可以实现并行

线程的基本使用

继承Thread类
实现Runnable接口
说明 Java是单继承机制 在某些情况下 一个类可能已经继承了某个父类 这时再用继承Thread类方法 来创建线程不可能 所以可以通过实现Runnable接口来创建线程
image

常用方法第二组
yield 线程的礼让 不一定成功
join 线程的插队 插队的线程一旦插队成功 则肯定先执行完插入的线程所有的任务
用户线程 也叫工作线程 当线程的任务执行完 或 通知方式结束
守护线程 一般是为工作线程服务的 当所有的用户线程结束 守护线程自动结束
常见的守护线程:垃圾回收机制

线程的生命周期

NEW 尚未启动的线程处于该状态
RUNNABLE 在Java虚拟机中执行的线程处于此状态 (READY RUNNING)
BLOCKED 在阻塞等待监视器锁定的线程处于此状态
WAITING 正在等待另一个线程执行特定动作的线程处于此状态
TIMED_WAITING正在等待另一个线程执行动作达到指定等待时间的线程处于该状态
TERMINATED 已退出的线程处于此状态
image

线程的同步

在多线程编程中 一些敏感数据不允许被多个线程同时访问 此时就使用同步访问计数 保证数据在任何同一时刻 最多有一个线程访问 以保证数据的完整性
理解 线程同步 即当有一个线程在对内存进行操作时 其他线程都不可以对这个内存地址进行操作 知道该线程完成操作 其他线程才能对该内存地址进行操作

同步具体方法
1. 同步代码块
synchronized(对象) {
得到对象的锁 才能操作同步代码
}
2.同步方法
public synchronized void m(String name) {

}

互斥锁

介绍

  1. Java中 引入对象互斥锁的概念 来保证共享数据操作的完整性
  2. 每个对象都对应了一个可称为"互斥锁"的标记 这个标记用来保证在任一时刻 只能有一个线程访问该对象
  3. 关键字synchronized来与对象的互斥锁联系 当某个对象用synchronized修饰时 表明该对象在任一时刻只能由一个线程访问
  4. 同步的局限性 导致程序的执行效率要降低
  5. 同步方法(非静态的)的锁可以是this 也可以是其他对象
  6. 同步方法(静态的) 的锁为当前类本身

同步方法如果没有使用static修饰 默认锁对象为this
如果方法使用static修饰 默认锁对象 当前类.class

线程的死锁

多个线程都占用了对方的锁资源 不肯礼让 导致了死锁

模拟线程死锁

点击查看代码
public class DeadLock_ {
    public static void main(String[] args) {
        DeadLockDemo A = new DeadLockDemo(true);
        A.setName("A 线程");
        DeadLockDemo B = new DeadLockDemo(false);
        B.setName("B 线程");
        A.start();
        B.start();
    }
}
@SuppressWarnings({"all"})
class DeadLockDemo extends Thread {
    static Object o1 = new Object();// 保证多线程,共享一个对象,这里使用 static
    static Object o2 = new Object();
    boolean flag;

    public DeadLockDemo(boolean flag) {//构造器
        this.flag = flag;
    }

    @Override
    public void run() {
//下面业务逻辑的分析
//1. 如果 flag 为 T, 线程 A 就会先得到/持有 o1 对象锁, 然后尝试去获取 o2 对象锁
//2. 如果线程 A 得不到 o2 对象锁,就会 Blocked
//3. 如果 flag 为 F, 线程 B 就会先得到/持有 o2 对象锁, 然后尝试去获取 o1 对象锁
//4. 如果线程 B 得不到 o1 对象锁,就会 Blocked
        if (flag) {
            synchronized (o1) {//对象互斥锁, 下面就是同步代码
                System.out.println(Thread.currentThread().getName() + " 进入 1");
                synchronized (o2) { // 这里获得 li 对象的监视权
                    System.out.println(Thread.currentThread().getName() + " 进入 2");
                }
            }
        } else {
            synchronized (o2) {
                System.out.println(Thread.currentThread().getName() + " 进入 3");
                synchronized (o1) { // 这里获得 li 对象的监视权
                    System.out.println(Thread.currentThread().getName() + " 进入 4");
                }
            }
        }
    }

释放锁

下面操作会释放锁
1.当前线程的同步方法 同步代码块执行结束
2.当前线程在同步代码块 同步方法中遇到break return
3.当前线程在同步代码块 同步方法中出现了未处理的Error 或 Exception 导致异常结束
4.当前线程在同步代码块 同步方法中执行了 线程对象的wait方法 当前线程暂停 并释放锁

下面操作不会释放锁
1.线程执行同步代码块或同步方法时 程序调用Thread.sleep() Thread.yield()方法 暂停当前线程的执行 不会释放锁
2.线程执行同步代码块时 其他线程调用了该线程的suspend()方法 将该线程挂起 该线程不会释放锁
方法不再推荐使用

IO流

文件
文件在程序中是以流的方式进行操作的
image

image

File类 最基本的文件的相关类

//方式1 new File(String pathname) //根据路径构建一个File对象
@Test
public void create01() {
    String fliePath = "C:\\let's get it\\JAVA\\chapter16\\news.txt";
    File file = new File(fliePath);
    try {
        file.createNewFile();//创建文件方法
        System.out.println("success");
    } catch (IOException e) {
        e.printStackTrace();
    }
}
//方式2 new File(File parent, String child) //根据父目录文件+子路径构建
//
@Test
public void create02() {
    File parentfile = new File("C:\\let's get it\\JAVA\\chapter16");
    String fileName = "news1.txt";
    //这里的file对象 在Java程序中 只是一个对象
    //只有执行了 createNewFile 方法 才会真正地在磁盘创建该文件
    File file = new File(parentfile, fileName);
    try {
        file.createNewFile();
        System.out.println("success");
    } catch (IOException e) {
        e.printStackTrace();
    }
}

//方式3 new File(String parent , String child)//根据父目录+子路径构建
@Test
public void create03() {
    String parantPath = "C:\\let's get it\\JAVA\\chapter16";
    String fileName = "new2.txt";
    File file = new File(parantPath , fileName);
	try {
        file.createNewFile();
    } catch (IOException e) {
        e.printStackTrace();
    }
  }
}

最基本的文件相关方法

点击查看代码

        //调用相应的方法 得到对应信息
        System.out.println("文件名 = " + file.getName());
        //绝对路径
        System.out.println("文件绝对路径 = " + file.getAbsolutePath());
        //父级目录
        System.out.println("文件父级目录 = " + file.getParent());
        //文件大小
        System.out.println("文件大小 = " + file.length());
        //文件是否存在
        System.out.println("文件是否存在 = " + file.exists());
        //是不是一个文件
        System.out.println("是不是一个文件 = " + file.isFile());
        //是不是一个目录
        System.out.println("是不是一个目录 = " + file.isDirectory());

image

节点流 处理流

节点流 可以从一个特定的数据源读写数据
处理流(也叫包装流) 是 连接在已存在的流 (节点流或者处理流之上) 为程序提供更为强大的读写功能 也更加灵活
处理流(包装流)包装节点流 既可以消除不同节点流的实现差异 也可以提供更方便的方法来完成输入输出
处理流对节点流包装 使用了修饰器的设计模式 不会与数据源直接相连
image

节点流 是低级流/底层流 直接跟数据源相连

FileInputStream

 String filePath = "xx\\xx\\xx";
    int readData = 0;
    FileInputStream fileInputStream = null;
    try {
        //创建 FileInputStream 对象 用于读取文件
        fileInputStream =  new FileInputStream(filePath);
        //从该输入流读取一个字节的数据 如果没有输入可用 此方法将停止
        //如果返回-1 表示读取完毕
        while ((readData = fileInputStream.read()) != -1) {
            System.out.print((char)readData);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        //关闭文件流 释放资源
        try {
            fileInputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
}

FileOutputStream

//创建 FileOutputSteam对象
    String filePath = "xx\\xx\\xx";
    FileOutputStream fileOutputStream = null;
    //new FileOutputStream(filePath) 创建方式 当写入内容 会覆盖原来的内容
    //new FileOutputStream(filePath , true) 创建方式 当写入内容 会追加到文件的后面
    try {
        // 得到 fileOutputStream 对象
        fileOutputStream = new FileOutputStream(filePath, true);
        //写入一个字节
        fileOutputStream.write('z');
        String str = "hello world";
        //str.getBytes 可以把字符串 --- 字节数组
        fileOutputStream.write(str.getBytes());
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        //关闭文件流 释放资源
        try {
            fileOutputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

FileReader

	String filePath = "xx\\xx\\xx";
    FileReader fileReader = null;
    int data = ' ';
    // 1. 创建FileReader对象
    try {
        fileReader = new FileReader(filePath);
        //循环读取 使用read
        while((data = fileReader.read()) != -1) {
            System.out.print((char)data);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }

FileWriter

String filePath = "xx\\xx";
    //创建FileWriter对象
    FileWriter fileWriter = null;
    char chars[] = {'a', 'b', 'c'};
    try {
        fileWriter = new FileWriter(filePath);//默认是覆盖写入
        //write(int):写入单个字符
        fileWriter.write('H');
        //write(char[]): 写入指定数组
        fileWriter.write(chars);
        //write(char[] , off , len):写入指定数组的指定部分
        fileWriter.write("哈哈哈哈哈".toCharArray(), 0, 3);
        //write(string):写入整个字符串
        fileWriter.write(" 你好哈哈哈哈");
        //write(string,off,len):写入字符串的指定部分
        fileWriter.write("上海天津",0,2);
        //在数据量大的情况下 可以使用循环操作
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            //对于FileWriter 一定要关闭流 或者flush才能真正的把数据写入到文件
            fileWriter.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

处理流

处理流的好处
性能的提高:主要以增加缓冲的方式来提高输入输出的效率
操作的便捷:处理流可能提供了一系列便捷的方法来一次输入输出大批量的数据使用更加灵活方便

BufferedReader 字符流

	String filePath = "C:\\Users\\19665\\Desktop\\Java相关\\caiquan.java";
	
    //创建bufferedReader
    BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath)); FileReader是访问文件方法的基类节点流
    String line; //按行读取 效率高

    while ((line = bufferedReader.readLine()) != null) {
        //按行读取文件 返回null时 表示文件读取完毕
        System.out.println(line);
    }
    //关闭流 这里注意 只需要关闭BufferedReader 底层会自动关闭节点流
    bufferedReader.close();
    /* 源代码
     public void close() throws IOException {
    synchronized (lock) {
        if (in == null)
            return;
        try {
            in.close();  in就是我们传入的fileReader
        } finally {
            in = null;
            cb = null;
        }
    }
}
     */

BufferedWriter 字符流

	String filePath = "C:\\Users\\19665\\Desktop\\Java相关\\ok.txt";
    //new FileWriter(filePath,true)表示以追加的方式写入
    //new FileWriter(filePath) 表示以覆盖的方式写入
    BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(filePath)); FileWriter是访问文件的抽象基类 节点流
    bufferedWriter.write("hhh");
    bufferedWriter.newLine();//插入一个换行
    bufferedWriter.write("www");
    bufferedWriter.newLine();//插入一个换行
    bufferedWriter.write("zzz");
    bufferedWriter.newLine();//插入一个换行
    bufferedWriter.close();

BufferedInputStream 字节流

BufferedOutputStream 字节流

对象流

序列化 反序列化
序列化就是保存数据 保存数据的值和数据类型
反序列化就是恢复数据 恢复数据的值和数据类型
需要让某个对象支持序列化机制 则必须让其类是可序列化的 要让某个类可序列化 该类必须实现 Serializable

ObjectOutputStream 提供序列化功能

		//序列化后 保存的文件格式 不是纯文本 而是按照他的格式来保存
		String filePath = "xx\\xx";
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath));

		//序列化数据到 ok1.dat
		oos.writeInt(100); // int --> Integer(implement Serializable)
		oos.writeBoolean(true);;// boolean -> Boolean (实现了 Serializable)
		oos.writeChar('a');// char -> Character (实现了 Serializable)
		oos.writeDouble(9.5);// double -> Double (实现了 Serializable)
		oos.writeUTF("嘿嘿");//String
		//保存一个 dog 对象
		oos.writeObject(new Dog("旺财", 10, "日本", "白色"));
		oos.close();
		System.out.println("数据保存完毕(序列化形式)");

ObjectInputstream 提供反序列化功能

	String filePath = "xx\\xx";
	ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath));

	System.out.println(ois.readInt());
	System.out.println(ois.readBoolean());
	System.out.println(ois.readChar());
	System.out.println(ois.readDouble());
	System.out.println(ois.readUTF());
	Object dog = ois.readObject();
	System.out.println("运行类型 = " + dog.getClass());
	System.out.println("dog信息 = " + dog);

	//重要细节

	//1.如果我们希望调用Dog的方法 需要向下转型
	//2.需要将Dog类的定义拷贝到可以引用的位置
	Dog dog2 = (Dog)dog;
	System.out.println(dog2.name);

	ois.close();

注意事项

  • 读写顺序要一致
  • 要求序列化或反序列化对象 需要实现Serializable
  • 序列化的类种建议添加SerialVersionUID 为了提高版本的兼容性
  • 序列化对象时 默认将里面所有属性都进行序列化 但除了static 或 transient 修饰的成员
  • 序列化对象时 要求里面属性的类型也需要实现序列化接口
  • 序列化具有可继承性 如果某类已经实现可序列化 则他的子类默认实现可序列化

标准输入输出流

// System 类的 public final static InputStream in = null;
// System.in 编译类型 InputStream
// System.in 运行类型 BufferedInputStream
// 表示标准输入 键盘
System.out.println(System.in.getClass());

// System.out public final static PrintStream out = null;
// 编译类型 PrintStream
// 运行类型 PrintStream
// 表示标准输出 显示器
//System.out.println(System.out.getClass());

转换流

image

InputStreamReader

	String filePath = "\\xx\\xx";
    // FileInputStream 转成 InputStreamReader
    // 指定编码 gbk
    // InputStreamReader isr = new InputStreamReader(new FileInputStream(filePath), "gbk");
    // InputStreamReader 传入 BufferedReader
    // BufferedReader br = new BufferedReader(isr);

    //合并
    BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(filePath), "gbk"));
    // 读取
    String s = br.readLine();
    System.out.println("读取内容 " + s);
    br.close();

OutputStreamWriter

	String filePath = "\\xx\\xx";
    // FileInputStream 转成 InputStreamReader
    // 指定编码 gbk
	//InputStreamReader isr = new InputStreamReader(new FileInputStream(filePath), "gbk");
    // InputStreamReader 传入 BufferedReader
	//BufferedReader br = new BufferedReader(isr);

    //合并
    BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(filePath), "gbk"));
    // 读取
    String s = br.readLine();
    System.out.println("读取内容 " + s);
    br.close();

Properties
image

网络编程

网络的相关概念

image

ip地址

  1. 概念 用于唯一标识网络中的每台计算机/主机
  2. 查看ip地址 ipconfig
  3. ip地址的表示形式:点分十分制 xx.xx.xx.xx
  4. 每一个十进制数的范围: 0 ~ 255
  5. ip地址的组成 = 网络地址 + 主机地址
    image

域名

image

网络通信协议

TCP UDP
TCP协议: 传输控制协议
1.使用TCP协议前,须先建立TCP连接,形成传输数据通道
2.传输前,采用"三次握手"方式,是可靠的
3. TCP协议进行通信的两个应用进程:客户端、服务端
4. 在连接中可进行大数据量的传输
5. 传输完毕,需释放已建立的连接,效率低

UDP协议:用户数据协议

  1. 将数据、源、目的封装成数据包,不需要建立连接
  2. 每个数据报的大小限制在64K内, 不适合传输大量数据
  3. 因无需连接,故是不可靠的
  4. 发送数据结束时无需释放资源

InetAddress类

相关方法
image

Socket类

image

TCP网络编程

image

字节流举例
SERVER端

public static void main(String[] args) throws IOException {
//在本机的 9999 端口监听 等待连接
//要求在本机没有其他服务在监听9999
//ServerSocket 可以通过accept 返回多个 Socket[多个客户端连接服务器的并发]
ServerSocket serverSocket = new ServerSocket(9999);
		
    //当没有客户端连接9999端口 程序会阻塞 等待连接
    //如果 有客户端连接 则会返回Socket对象 程序继续
    Socket socket = serverSocket.accept();

    //通过socket.getInputStream() 读取客户端写入到数据通道的数据
    InputStream is = socket.getInputStream();
    byte buf[] = new byte[1024];
    int Len = 0;
    while ((Len = is.read(buf)) != -1) {
        System.out.println((new String(buf, 0, Len)));
    }


    OutputStream os = socket.getOutputStream();
    os.write("hello,client".getBytes());
    socket.shutdownOutput();


    //关闭流和socket
    os.close();
    is.close();
    socket.close();
    serverSocket.close();

}

CLIENT端

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

    //连接服务器 (ip,端口)
    //连接本机的9999端口  如果连接成功 返回 Socket
    Socket socket = new Socket(InetAddress.getLocalHost(), 9999);


    //连接上后  生成socket 通过socket.getOutputStream()
    //    得到 和 socket对象 关联的输出流对象
    //
    // 通过输出流 写入数据到 数据通道
    OutputStream os = socket.getOutputStream();
    os.write("hello,server".getBytes());

    socket.shutdownOutput();//设置结束标记

    InputStream is = socket.getInputStream();
    byte buf[] = new byte[1024];
    int Len = 0;
    while ((Len = is.read(buf)) != -1) {
        System.out.println(new String(buf,0,Len));
    }

    is.close();
    os.close();
    socket.close();


}

字符流举例
SERVER端

	//在本机的 9999 端口监听 等待连接
    //要求在本机没有其他服务在监听9999
    // ServerSocket 可以通过accept 返回多个 Socket[多个客户端连接服务器的并发]
    ServerSocket serverSocket = new ServerSocket(9999);
    System.out.println("服务端 在9999端口监听 等待连接");

    //当没有客户端连接9999端口 程序会阻塞 等待连接
    //如果 有客户端连接 则会返回Socket对象 程序继续
    Socket socket = serverSocket.accept();

    //通过socket.getInputStream() 读取客户端写入到数据通道的数据
    InputStream is = socket.getInputStream();
    BufferedReader br = new BufferedReader(new InputStreamReader(is));
    String s = br.readLine();
    System.out.println(s);


    OutputStream os = socket.getOutputStream();

    BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os));
    bw.write("hello client 字符流");
    bw.newLine();//插入一个换行符 表示回复内容的结束
    bw.flush();

    socket.shutdownOutput();


    //关闭流和socket
    bw.close();
    br.close();
    socket.close();
    serverSocket.close();//关闭

}

CLIENT端

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

    //连接服务器 (ip,端口)
    //连接本机的9999端口  如果连接成功 返回 Socket
    Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
    System.out.println("客户端 socket 返回 = " + socket.getClass());

    //连接上后  生成socket 通过socket.getOutputStream()
    //    得到 和 socket对象 关联的输出流对象
    OutputStream os = socket.getOutputStream();
    //通过输出流 写入数据到 数据通道
    BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os));
    bw.write("hello server 字符流~");
    bw.newLine();//插入一个换行符 表示写入内容的结束 要求对方 使用readline() !!!
    bw.flush();//如果使用的字符流 需要手动刷新 否则数据不会写入数据通道


    InputStream is = socket.getInputStream();
    BufferedReader br = new BufferedReader(new InputStreamReader(is));
    String s = br.readLine();
    System.out.println(s);

    bw.close();
    br.close();
    socket.close();
}

UDP网络编程

image
SENDER端

public static void main(String[] args) throws IOException {
    //创建 DatagramSocket 对象 准备在9998接收数据
    DatagramSocket socket = new DatagramSocket(9998);

    //将需要发送的数据 封装到 DatagramPacket对象
    byte data[] = "hello 明天吃火锅~".getBytes();

    //封装的 DatagramPacket 对象 data 内容字节数组 data.length 主机 端口
    DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getLocalHost(),9999);
    socket.send(packet);

    byte buf[] = new byte[1024];
    DatagramPacket packet1 = new DatagramPacket(buf, buf.length);

    //调用接收方法 将通过网络传输的DatagramPacket对象
    //填充到packet对象
    //当有数据包发送到 本机的9999端口 就会接收到数据
    //如果没有数据包发送到 9999端口 就会阻塞
    System.out.println("发送端B 等待接收数据~");
    socket.receive(packet1);

    int length = packet1.getLength(); //实际接受的数据长度
    byte data1[] = packet1.getData(); //接收到数据
    String s = new String(data1, 0, length);
    System.out.println(s);


    socket.close();
    System.out.println("B exit");
}

RECIVER端

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

    //创建一个 DatagramSocket 对象 准备在9999接收数据
    DatagramSocket socket = new DatagramSocket(9999);

    //构建一个DatagramPacket对象 准备接受数据
    //在前面讲解UDP时 老师说过一个数据包最大 64K
    byte buf[] = new byte[1024];
    DatagramPacket packet = new DatagramPacket(buf, buf.length);

    //调用接收方法 将通过网络传输的DatagramPacket对象
    //填充到packet对象
    //当有数据包发送到 本机的9999端口 就会接收到数据
    //如果没有数据包发送到 9999端口 就会阻塞
    System.out.println("接收端A 等待接收数据~");
    socket.receive(packet);

    //可以把packet进行拆包 取出数据 并显示
    int length = packet.getLength(); //实际接受的数据长度
    byte data[] = packet.getData(); //接收到数据
    String s = new String(data, 0, length);
    System.out.println(s);


    //将需要发送的数据 封装到 DatagramPacket对象
    byte data1[] = "好的 明天见 ".getBytes();
    //封装的 DatagramPacket 对象 data 内容字节数组 data.length 主机 端口
    DatagramPacket packet1 = new DatagramPacket(data1, data1.length, InetAddress.getLocalHost(),9998);
    socket.send(packet1);


    socket.close();
    System.out.println("A exit");

}

posted on 2022-01-01 21:45  玛卡巴卡学编程  阅读(116)  评论(1)    收藏  举报

导航