1、等待唤醒机制
wait和notify/notifyAll的执行原理:
wait:
线程不再活动,不再参与调用,进入wait set中,因此不会浪费CPU资源,也不会去竞争锁,这是的线程状态是"WAITTING",它还要等着别的线程执行"通知(notify)",让在锁对象上等待的线程从wait set中释放出来,重新进入调度队列(ready queue)中
notify/notifyAll:
哪怕只通知了一个等待的线程,被通知的线程也不能立即回复执行,因为它当初中断的地方是在同步代码块中,而此刻它已经不持有锁,所以它需要"再次尝试去获取锁"(很可能面临其他线程的竞争),成功后才能在当初调用wait()之后的地方回复执行
总结如下:
如果能获取锁,线程就从"WAITTING"状态变成"RUNNABLE"状态否则,从wait set出来,线程就从 "WAITTING"状态又变成"BLOCKED"状态
调用wait()和notify()需要注意的细节:
1、wait()与notify()必须要由"同一个锁对象"调用
因为对应的锁对象可以通过notify()唤醒使用同一个锁对象调用的wait()后的线程
2、wait()与notify()是属于Object类的方法
因为锁对象可以是任意对象,而任意对象的所属类都是继承了Object类的
3、wait()与notify()必须要在"同步代码块"或者"同步方法"中使用
因为必须通过所对象调用这2个方法
2、线程池
线程池的工作原理:
提前创建号多个线程对象,放在集合中,多个任务来了反复使用这些线程对象来执行
线程池的创建和执行步骤:
1、使用Executors的静态方法newFixedThreadPool(int nThread)创建线程池ExecutorService
2、创建一个任务类,实现Runnable接口,重写run()方法
3、调用ExcutorService对象的submit(Runnable task)方法,传递任务给线程池,执行任务。
4、调用ExcutorService对象的shutdown()方法,销毁线程池
// 创建线程池对象
ExecutorService es = Executors.newFixedThreadPool(2);
// 提交任务
RunnableImpl r = new RunnableImpl(); // 1个任务
es.submit(new RunnableImpl());
es.submit(new RunnableImpl());
es.submit(new RunnableImpl());
// 销毁线程池
es.shutdown();
3、Lambda表达式:
函数式接口:"有且仅有一个抽象方法的接口"
抽象方法:1、有方法体的方法"不算作"抽象方法,如默认方法,静态方法,私有方法
2、 如果一个抽象方法 与 java.lang.Object类中的方法 定义相同的, 也"不算作"抽象方法
省略原则:
1、参数列表:参数”类型“可以省略(a,b)->{}
2、参数列表:如果参数只有1个,则"类型"和"小括号"都可以省略a->sout(a)
3、一些代码:如果只有一条代码,则”大括号“,“returen”,”分号“都可以”一起省略“
Lambda表达式的使用前提:
1、Lambda只能用于接口,且"接口中有且仅有一个抽象方法"(函数式接口)
2、使用Lambda必须具有上下文推断
4、File类
绝对路径:以盘符开始的路径
相对路径:不以盘符开始的简化路径
注意事项:1、路径不区分大小写;2、路径一般写成字符串,而字符串中一个\是转义,所以要写两个\\
常用方法:String getAbsolutePath()、String getPath()、String getName()、long length()、boolean exists、boolean isDirectory、boolean isFile、boolean createNewFile、boolean delete、boolean mkdir()、boolean mkdirs()、String[] list()、File[] listFiles
5、递归
递归的注意事项:
1、递归要有限定条件(出口),保证递归能够停止,否则栈内存溢出
2、递归次数不能太多,否则栈内存溢出
3、构造方法不能递归
6、字节流
常用方法:void close()、void flush() 、abstract void write(int b)、void write(byte[] b)、void write(byte[] b, int offset, int len)、FileOutputStream(String name)、FileOutputStream(File file)
构造方法的作用:
1、创建一个FileOutputStram对象
2、根据构造方法传递的路径,在磁盘上创建一个空文件("如果文件存在则会清空数据")
3、将创建的FileOutputStram对象指向这个磁盘文件
使用字节输出流写数据到文件的步骤:
1. 创建对象: 创建FileOutputStream对象, 构造方法中传递文件路径
2. 写入数据: 使用FileOutputStream对象调用 write(int b) 方法, 将字节写入文件
3. 释放资源: 使用FileOutputStream对象调用 close() 方法, 关闭流对象释放资源
FileOutputStream(String name, boolean append): 通过文件路径创建流, true可以续写
FileOutputStream(File file, boolean append): 通过File对象创建流, true可以续写
字节输出流:
构造方法的作用:
1、创建一个FileOutputStream对象
2、根据构造方法传递的路径,在磁盘上创建一个空文件
3、将创建的FileOutputStream对象指向这个磁盘文件
字节输出流: 一次写一个字节到文件
1. 创建对象: 创建FileOutputStream对象, 构造方法中传递文件路径
2. 写入数据: 使用FileOutputStream对象调用 write(int b) 方法, 将字节写入文件
3. 释放资源: 使用FileOutputStream对象调用 close() 方法, 关闭流对象释放资源
字节输入流: 一次读取一个字节
FileInputStream读数据步骤:
1. 创建FileInputStream对象
2. 使用FileInputStream对象, 调用 int read() 方法, 一次读取一个byte
3. 释放资源
字符输出流: 一次写一个字符
1.创建FileWriter对象, 构造方法中绑定要写入数据的目的地
2.使用FileWriter中的方法 write(), 把数据写入到"内存缓冲区"中(字符转换为字节的过程)
3.使用FileWriter中的方法 flush(), 把内存缓冲区中的数据,"刷新到文件中"
4.释放资源 close() (会先把内存缓冲区中的数据刷新到文件中)
7、Properties
常用方法:Properties()、Object setProperty(String key, String value)、String getProperty(String key)、Set<String> stringPropertyNames()、void store(OutputStream out, String comments)、void store(Writer writer, String comments)、void load(InputStream inStream)、void load(Reader reader)
使用步骤:
1、创建Properties 集合对象,添加数据
2、创建字节/字符输出流对象,构造方法中绑定要输出的目的地
3、使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中储存
4、释放资源
Properties: load()
使用步骤:
1.创建Properties集合对象
2.使用Properties集合对象中的方法load读取保存键值对的文件
3.遍历Properties集合
8、缓冲流
缓冲流的原理:
1、底层也是使用基本流来读写
2、但缓冲流内部定义了一个缓冲数组,在读的时候类似于我们一次读一个数组的方式,减少了磁盘操作次数,提高了程序效率
BufferedOutputStream使用步骤:
1.创建FileOutputStream对象, 构造方法中绑定要输出的目的地
2.创建BufferedOutputStream对象, 构造方法中传递FileOutputStream对象
3.使用BufferedOutputStream对象中的方法 write(), 把数据写入到内部缓冲区中
4.使用BufferedOutputStream对象中的方法 flush(), 把内部缓冲区中的数据,刷新到文件中
5.释放资源(会先调用flush方法刷新数据, 第4步可以省略)
BufferedInputStream使用步骤:
1.创建FileInputStream对象, 构造方法中绑定要读取的数据源
2.创建BufferedInputStream对象, 构造方法中传递FileInputStream对象
3.使用BufferedInputStream对象中的方法 read(), 读取文件
4.释放资源 close()
9、乱码问题
乱码原因: 读写编码不一致
GBK文件中存储的是"你好"在GBK中对应的byte
而IDEA中使用FileReader读取文件时, 是将byte按照UTF-8编码表查找字符, 结果找不到, 就显示了问号
serialVersionUID序列号的作用:
序列化操作时, 会根据类生成一个默认的 serialVersionUID 属性, 写入到文件中.
该版本号是根据类中成员计算出来的一个数值. 当类的成员发生改变时, 序列版本号也会发生变化
当代码中的类和读取的对象序列版本号不一致时, 就会抛出InvalidClassException
10、打印流PrintStream:
PrintStream特点:
1. 只有输出流, 没有输入流
2. PrintStream不会抛出IOException
3. 有特殊方法 print(), println(), 可以输出任意类型的值, 原样输出
注意事项:
如果用 write(97) 方法, 会查编码表 97 -> a
如果用 print(97), println(97), 则原样输出 97 int -> '9''7' -> byte -> 文件 97
System.out.println();
PrintStream out;