本周学习了多线程、数据安全锁、网络通信编程
这篇记录多线程
开篇留一个问题:线程中sleep方法的作用是让当前线程暂停,其他线程继续,还是就是仅仅暂停
答:
sleep方法让线程暂停的意义是等待控制台输入信息,进行操作的变更(idea中直观的感受);
或者线程切换
一.多线程的实现3种方法
线程的启动:都是调用start方法,调用具体的run方法并不是启动线程
线程的功能:run方法中的语句就是线程中具体的操作,开一个线程为了做什么都写在run方法中
(方法1)重写Thread类的run方法:可以直接用匿名对象的方式,创建对象,后面用lambda表达式写抽象方法run的具体实现
(方法2)重写Runnable接口的run方法:还是需要创建线程对象,这个线程对象构造参数传入的是一个接口对象,这个接口实现的抽象父接口叫Runnable,同时实现的父接口抽象方法还是叫run(),所以创建线程之后,还是用线程对象调用.start()方法来启动线程
这里引入数据安全问题,解决办法是用锁,两种:
synchronized代码块
->对象:必须是同一个对象,必须有名字(不能直接写new创建匿名对象)
->代码块:this关键字指向的对象
->静态方法:字节码文件,一般是类名.class,比如自己用来实现Runnable接口的子接口名是MyRunnable,那么synchronized括号里面参数就写MyRunnable.class
Lock类调用ReentrantLock构造方法创建对象然后调用lock和unlock方法
之后因为使用两次嵌套synchronized关键字引入死锁现象
问题描述:定义了对象类,充当锁对象括号内的参数,但是有两个,两个实现Runnable接口的子接口对象,继续创建出对应的线程对象
然后调用启动方法,产生死锁
->解决办法1:更改加锁顺序。如果加锁逻辑在if分支中不一样,就修改成一样的嵌套顺序。原因未知,自己还没理解
->解决办法2:再加一个锁对象,锁之前的if分支,使得两个分支被同一个对象锁操作
(方法3)重写Callable接口的call方法
这篇记录只是大纲,代码其实需要自己运行添加解读注释,还需要深入理解
目前突然想到的薄弱记忆点:
深度克隆和浅克隆:涉及内部类的复制问题
文件流的操作:
1.创建文件是用文件名还是全地址?点斜杠文件名表示相对目录创建可以在idea项目同级根目录直接看见,直接在idea界面查看
2.字节流读取、写回:
官方定义是输入输出,但是自己理解如下:
输入:从文件读取到内存,等价于读取字符串输出到控制台。当然这个是在idea自己看见的一般形式
输出:从内存写回到文件,等价于把赋值结束后的【字节数组或者字符行】作为参数用write方法输出到文件
目前严重混淆的是一个创建对象的嵌套调用形式:连续两个new方法嵌套,为了一步完成创建字节流或者字符流的输入输出流
这样对于输出流要添加append关键字不好明确位置,需要自己从最内层找到文件基础流,然后写逗号true
但是可以明确:外层到内层的嵌套,其实是一个子类构造方法,传入的参数是父类继续调用构造方法,父类继续调用最基础的文件对象创建方法
下面是一些见到的代码例子:括号外是子类,括号内是父类,最内层的是文件对象
<1>
缓冲字符流(字节字符转换流(文件对象))
package com.cskaoyan.tcp.v2;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @description:
* @author: 景天老师
**/
public class Server2 {
public static void main(String[] args) throws Exception{
//【1.】创建服务端Serversocket对象,指定自己的通信端口 11111
ServerSocket serverSocket = new ServerSocket(11111);
while (true) {
//为什么这里不用字节数组,因为是字符流,我们在控制台输入汉字
//byte[] bytes = new byte[1024];
//int readCount = in.read(bytes);
//String s = new String(bytes, 0, readCount);
//【2.】监听连接请求
//服务器端套接字一直用自己的 11111 端口监听
//监听到请求,就创建客户端套接字对象,存储监听到的套接字,后面调用套接字方法
Socket socket = serverSocket.accept();//之后用这个客户端套接字来调用收发
//【3.】用客户端套接字调用返回输入流方法,是服务器端读客户端内容到这个流
InputStream in = socket.getInputStream();
//最内层是字节向字符转换
//外层是缓冲字符流
//需要理解这种嵌套定义的父类子类关系:
//缓冲字符流,为了用读取行方法。参数是套接字返回的输入流
BufferedReader br = new BufferedReader(new InputStreamReader(in));
//字符串变量s接收读取到的行,向服务器端输入
String s = br.readLine();
//返回客户端ip地址字符串
InetAddress inetAddress = socket.getInetAddress();
//返回客户端通信端口
int port = socket.getPort();
//【4.】拼接信息字符串
System.out.println("接收到了来自" + inetAddress + ":" + port + "的消息: " + s);
}
}
}
<2>
缓冲字符流(文件字符流(文件名))
package com.cskaoyan.buffer;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
/**
* @description:
* @author: songtao@cskaoyan.onaliyun.com
**/
public class Demo {
public static void main(String[] args) throws IOException {
//创建缓冲输出流
//BufferedWriter,用Writer是字符,
//在这里BufferedWriter的参数是新建了FileWriter字符流输出流对象
//一个char数组,默认大小8192,总计16kB
BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt"));
bw.write("选择正确");
//除了Writer的5种方法,新增加了newLine()换行方法
bw.newLine();
bw.write("才是正确");
bw.close();
}
}
<3>
字节字符转换流(文件字符流(文件名))
package com.cskaoyan.bytestream.transf;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
/**
* @description: 转换流
* @author: 景天老师
**/
public class Demo {
public static void main(String[] args) throws IOException {
//创建转化输出流对象
//【字符】
OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream("a.txt"));
//写数据
String s = "今日进度:校对需求文档大纲";
//writeSingle(out, s);
//多字符
//writeMuti(out, s);
//直接写字符串
out.write(s,0,s.length());
String encoding = out.getEncoding();//获取输出流的编码
System.out.println(encoding);
out.flush();
//close
out.close();
}
private static void writeMuti(OutputStreamWriter out, String s) throws IOException {
char[] chars = s.toCharArray();//转String到字符数组
out.write(chars,0,chars.length);
}
private static void writeSingle(OutputStreamWriter out, String s) throws IOException {
char[] chars = s.toCharArray();//为了对比,所以写成for循环,每个字符数组元素作为参数
for (int i = 0; i < chars.length; i++) {
out.write(chars[i]);
}
}
}
<4>
字节字符转换流(文件字节输出流(文件名),charsetName)
可以指定字符集的:文件字节流,而且是读取
package com.cskaoyan.bytestream.transf;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* @description: 转换流读取数据
* @author: 景天老师
**/
public class Demo2 {
public static void main(String[] args) throws IOException {
// 创建转换输入流
InputStreamReader in = new InputStreamReader(new FileInputStream("a.txt"));
InputStreamReader in2 = new InputStreamReader(new FileInputStream("a1.txt"), "GBk");
// 读取数据
int readData1 = in.read();
System.out.println(((char) readData1));
//readSingle(in);
// 读取多个字符 读取的字符数,如果已到达流的末尾,则返回 -1
char[] chars = new char[1024];//读取输入流in内容到chars字符数组
int readCount1;//表示读取到的字符个数
while ((readCount1 = in.read(chars)) != -1) {
System.out.println(new String(chars,0,readCount1));
}
//readMuti(in);
in.close();
}
private static void readMuti(InputStreamReader in) throws IOException {
char[] chars = new char[1024];//读取输入流in内容到chars字符数组
int readCount;
while ((readCount = in.read(chars)) != -1) {
System.out.println(new String(chars,0,readCount));
}
}
private static void readSingle(InputStreamReader in) throws IOException {
int readData = in.read();
System.out.println(((char) readData));
int readData2 = in.read();
System.out.println(((char) readData2));
int readData3 = in.read();
System.out.println(((char) readData3));
int readData4 = in.read();
//System.out.println(((char) readData4));//换行
System.out.println(readData4);
//System.out.println((char)13);
}
}