第六周
Day26
异常
-
异常的概念
- 异常在运行过程中出现特殊的情况
- 异常处理的必要性:任何程序都可能存在大量的未知问题、错误;如果不对这些问题进行正确的处理,则可能导致程序的中断,造成不必要的损失
-
异常的分类
- Throwable:可抛出的,一切错误或异常的父类,位于java.lang包中。
- -Error:JVM 、硬件、执行逻辑错误,不能手动处理
- Exception:程序在运行和配置中产生的问题,可处理
- RuntimeException:运行时异常,可处理,可不处理
- CheckedException:受查异常,必须处理
-
异常 的产生
- 自动抛出异常:当程序值在运行时遇到不规范的代码或结果时,会产生异常
- 手动抛出异常:语法:throws new 异常类型(“实际参数”)
- 产生异常结果:相当于遇到return 语句。导致程序因异常而终止
-
异常的传递
- 概念:按照方法的调用链反向传递。如果始终没有处理异常,最终会由JVM进行默认异常处理(打印堆栈跟踪信息)
- 受查异常: throws 声明异常吧,修饰在方法参数列表后端
- 运行时异常:因可处理可不处理。无需声明异常
-
异常的处理
try {
可能出现异常的代码
}catch(Exception e ){
异常处理的相关代码,如 :getMessage()、printStackTrace()}finally{
无论是否出现异常,都需执行的代码结构,常用于释放资源
}
-
常见异常处理结构
- try{} catch {}
- try{} catch{}catch{}
- try{} catch{}finally{}
- try{}catch{}catch{] finally{}
- try{} finally
- 注意:多重catch,遵循从子(小)到父(大)的顺序,父类异常在最后
-
-
自定义异常
-
需继承自ERxception 或Exception 的子类,常用RuntimeException
-
必要提供的构造方法
- 无参数构造方法
- String message参数的构造方法
-
-
方法的覆盖
-
带有异常声明的方法覆盖
- 方法名、参数列表、返回值类型必须和父类相同
- 子类的访问修饰符合父类相同或是比父类更宽
- 子类中的方法,不能抛出比父类更多、更宽的异常
-
-
总结
-
异常的概念:程序在运行过程中出现的特殊情况
-
异常的分类
- RuntimeException:运行时异常、可处理、可不处理
- CheckedException:受查异常,必须处理
-
异常的产生
- throw new 异常类型(“实际参数”)
- 相当于遇到return 语句,导致程序因异常而终止
-
异常的传递
- 按照方法的调用链反向传递,如始终没有处理异常,最终会由jvm 进行默认异常处理(打印堆栈跟踪信息)
-
异常的处理
- try{} catch{} finally{}
-
带有异常声明的方法覆盖:
- 子类中的方法,不能抛出比父类更多、更宽的异常
-
Day27、Day28
多线程
-
线程的概念
- 运行时的程序,才被称为进程
- 线程,又轻量级进程。程序中的一个顺序控制流程,同时也是CPU的基本调度单位。进程由多个线程组成,彼此间完成不同的工作,交替执行,称为多线程
-
线程的组成
-
任何一个线程都具有基本的组成部分
-
CPU时间片:操作系统会为每个线程分配执行时间
-
运行数据:
- 堆空间:存储线程需使用的对象,多个线程可以共享堆中的对象
- 栈空间:存储线程需使用的局部变量,每个线程都拥有独立的栈
-
逻辑代码
-
-
创建线程的方式
-
创建线程的第一种方式
public class TestCreateThread {
public static void main(String[] args){
MyThread t1 = new MyThread();
t1.start();}
}
class MyThread extends Thread{
public void run(){
for (int i = 1;i<=50;i++){System.out.println("MyThread:L"+i);
}
}
}
-
创建线程的第二种方式:
public class TestCreateThread{
public static void main(String [] args){MyRunnable mr = new MyRunnable();
Thread t2 = new Thread(mr);
t2.start();}
}class MyRunnale implements Runnnale {
public void run (){ for (int i = 1;i<=50;i++){ System.out.println("MyRunnale:"+i);
}
}
}
-
-
线程的状态
-
New 初始状态
- 线程对象被创建,即为初始状态。只在堆中开辟内存,与常规对象无异
-
Ready就绪状态
- 调用start()之后,进入就绪状态。等待OS选中,并分配时间片
-
Running 运行状态
- 获得时间片之后,进入运行状态,如果时间片到期,则返回到就绪状态
-
Terminated终止状态
- 主线程main()或独立线程run()结束,进入终止状态, 并释放持有的时间片
-
-
常见方法
-
休眠
- public static void sleep(long millis)
- 当前线程主动休眠millis毫秒
-
放弃
- public static void yield
- 当前线程主动放弃时间片,回到就绪状态你,竞争下一次时间片
-
结合
- public final void join()
- 允许其他线程加入到当前线程中
-
-
线程的安全
-
需求
- A线程将“HeLlo”存入数组的第一空位;B线程将“world”存入数组的第一个空位
-
线程不安全
- 当多线程并发访问临界资源时,如果破坏原子操作,可能会造成数据不一致
- 临界资源:共享资源(同一对象),一次仅允许一个线程使用,才可保证其正确性
- 原子操作:不可分割的多步操作,被视作一个整体,其顺序和步骤不可打乱或缺省
-
-
同步方式
-
同步代码块
synchronized (临界资源对象){//对临界资源对象加锁
// 代码(原子操作)
}
-
注意
- 每个对象都有一个互斥锁标的标记,用来分配给线程的
- 只有拥有对象互斥锁标记的线程,才能进入对该对象加锁的同步代码块
- 线程退出同步代码块时,会释放相应的互斥锁标记
-
-
同步方式2
-
同步方法
synchronized 返回值类型 方法名称(形参列表()){// 对当前对象(this)加锁
//代码(原子操作)
}
-
注
- 只有拥有对象互斥锁标记的线程,才能进入该对象加锁的同步方法中
- 线程退出同步方法时,会释放相应的互斥锁标记
-
-
同步规则
-
注意
- 只有在调用包含同步代码的方法,或者同步方法时,才需要对象的锁标记
- 如调用不包含同步代码块的方法,或普通方法时,则不需要锁标记,可直接调用
-
已知jdk 中线程安全的类
- StringBuffer
- Vector
- Hashtable
- 以上的类中的公开方法,均为synchonized修饰的同步方法
-
-
经典问题
-
死锁
- 当第一个线程拥有A对象锁标记。并等待B对象锁标记,同时第二个线程拥有B对象锁标记,并等待 A 对象锁标记时,产生死锁
- 一个线程可以同时拥有多个对象的锁标记,当线程阻塞时,不会释放已经拥有的锁标记,由此可能造成死锁
-
生产者、消费者
- 若干个生产者在生产产品,这些产品将提供若干消费者去消费,为了使生产者和消费者能并发执行,在两者之间设置一个能存储多个产品的缓冲区,生产者将生产的产品放入缓冲区中,消费者从缓冲区中取走产品进行消费,显然生产者和消费者之间必须保持同步,即不允许消费者到一个空的缓冲区中取产品,也不允许生产者向一个慢的缓冲区中放入产品
-
-
线程通信
-
等待
- public final void wait()
- public final void wait(long timeout)
- 必须在对obj 加锁的同步代码块中。在一个线程中,调用obj.wait() 时。此线程会释放其拥有的所有锁标记。同时此线程阻塞在o 的等待队列中。释放 锁,进入等待队列
-
通知
- public final void notify()
- public final void notifyAll()
- 必须在对obj 加锁的同步代码块中。从obj 的Wating中释放一个或全部线程。对自身没有任何影响
-
-
总结
-
线程的创建
- 方式1:继承Thread类
- 方式2:实现Runnable 接口(一个任务Task),传入给Thread对象并执行
-
线程安全
- 同步代码块:为方法中的局部代码(原子操作)加锁
- 同步方法:为方法中的所有代码(原子操作)加锁
-
线程间的通信:
- wait(0 /wait(long timeout) 等待
- notify()/notifyAll():通知
-
-
线程池
-
现有问题
- 线程是宝贵的内存资源、单个线程约占1MB空间,过分分配易造成内存溢出。
- 频繁的创建及销毁线程会增加虚拟机回收效率、资源开销,造成程序性能下降
-
线程池
- 线程容器,可设定线程分配的数量上限
- 将预先创建的线程对象存入池中,并重用线程池中的线程对象
- 避免频繁的创建和销毁
-
-
线程池原理
- 将任务提交给线程池,由线程池分配线程、运行任务,并在当前任务结束后复用线程
-
获取线程池
- 常用的线程池接口和类(所在包java.util.concurrent)
- Executor:线程池的顶级接口
- ExecutorService:线程池接口,可通过submit(Runnale task )提交任务代码
- Executors工厂类:通过此类可以获得此类可以获得一个线程池
- 通过newFixedThreadPool(int nThreads)获取固定数量的线程池。参数:指定线程池中线程的数量
- 通过newCachedThreadPool() 获取动态数量的线程池,如不够则创建新的,没有上限
-
Callable接口
public interface Callable{
public V call () throws Exception;
}
- JDK5加入,与Runnale 接口类似,实现之后代表一个线程任务
- Callable具有泛型返回值、可以声明异常
-
Future 接口
- 概念:异步接收ExecutorService.submit()所返回的状态结果,当中包含了call() 返回值
- 方法: V get() 以阻塞形式等待Future中的异步处理结果(call() 的返回值)
-
线程的同步
- 形容一次方法调用,同步一旦开始。调用者必须等待该方法返回,才能继续
- 注意:单条路径
-
线程的异步
- 形容一次方法调用,异步一旦开始,像是一次消息传递,调用者告知之后立刻返回,二者竞争时间片,并发执行
- 注意:多条执行路径
-
Lock接口
-
jdk5 加入,与synchronized 比较,显示定义,结构更灵活
-
提供更多实用性方法,功能更强大、性能更优越
-
常用方法
- void lock() // 获取锁,如锁被占用。则等待
- boolean tryLock() //尝试获取锁(成功返回true。失败返回false ,不阻塞)
- void unlocck() // 释放锁
-
-
重入锁
- ReentrantLock:Lock接口的实现类,与synchronized 一样具有互斥锁功能
-
读写锁
-
ReentrantReadWriteLock
- 一种支持一写多读的同步锁,读写分离,可分别分配读锁,写锁
- 支持多次分配读锁,使多个读操作可以并发执行
-
互斥规则
-
写-写:
- 互斥,阻塞
-
读-写
- 互斥,读阻塞写,写阻塞读
-
读-读
- 不互斥、不阻塞
-
在读操作远远高于写操作的环境中,可保障线程安全的情况下,提高运行效率
-
-
day29,day30
<爱编码的程序员> 这个微信公众号有更多关于java知识的详解
I/O 框架部分
-
流的概念
- 内存与存储设备之间传输数据的通道
-
流的分类
-
按方向
-
输入流:
- 将<存储设备 >中的内容读入到<内存>中
-
输出流
- 将<内存>中的内容写入到<存储设备>中
-
-
按单位
-
字符流
- 以字符为单位,只能读写文本数据
-
字节流
- 以字节为单位,可以读写所有数据
-
-
按功能
-
节点流
- 具有实际传输数据的读写功能
-
过滤流
- 在节点流的基础之上增强功能
-
-
-
字节流
-
字节流的父类(抽象类)
-
InputSteam:字节输入流
- public int read()[]
- public int read(byte[] b){}
- public int read(byte[] b ,int off ,int len){}
-
outputStream:字节输出流
- public void write(int n){}
- public void write(byte[] b ){}
- public void write(byte[] b ,int off,int len){}
-
-
编码方法
-
字符流
-
File类