A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

day05:【异常】
异常的根类是 java.lang.Throwable ,其下有两个子类:java.lang.Error java.lang.Exception;
Throwable体系:
Error :严重错误Error,无法通过处理的错误,只能事先避免,好比绝症。
Exception :表示异常,异常产生后程序员可以通过代码的方式纠正,使程序继续运行,是必须要处理的。好
比感冒、阑尾炎。
Throwable中的常用方法:
public void printStackTrace() :打印异常的详细信息。
包含了异常的类型,异常的原因,还包括异常出现的位置,在开发和调试阶段,都得使用printStackTrace
public String getMessage() :获取发生异常的原因。提示给用户的时候,就提示错误原因。
public String toString() :获取异常的类型和异常描述信息(不用)
异常 (Exception)的分类:根据在编译时期还是运行时期去检查异常?
编译时期异常 :checked异常。在编译时期,就会检查,如果没有处理异常,则编译失败。(如日期格式化异常)
运行时期异常 :runtime异常。在运行时期,检查异常.在编译时期,运行异常不会编译器检测(不报错)(如数学异常)
Java异常处理的五个关键字trycatchfinallythrowthrows
抛出异常throw
1. 创建一个异常对象。封装一些提示信息(信息可以自己编写)
2. 需要将这个异常对象告知给调用者。怎么告知呢?怎么将这个异常对象传递到调用者处呢?通过关键字throw就可以完成。throw 异常对象。
注意:throw关键字,只能抛throwable类以及它子类(错误和异常)
使用格式hrow new 异常类名(参数);
例如:throw new NullPointerException("要访问的arr数组不存在");
throw new ArrayIndexOutOfBoundsException("该索引在数组中不存在,已超出范围");
声明异常:将问题标识出来,报告给调用者。如果方法内通过throw抛出了编译时异常,而没有捕获处理,那么必须通过throws进行声明,让调用者去处理。
关键字throws运用于方法声明之上,用于表示当前方法不处理异常,而是提醒该方法的调用者来处理异常(抛出异常).
声明异常格式:修饰符 返回值类型 方法名(参数) throws 异常类名1,异常类名2{   }
捕获异常trycatch
如果异常出现的话,会立刻终止程序,所以我们得处理异常:
1. 该方法不处理,而是声明抛出,由该方法的调用者来处理(throws)
2. 在方法中使用try-catch的语句块来处理异常。
try-catch的方式就是捕获异常。
捕获异常 Java中对异常有针对性的语句进行捕获,可以对出现的异常进行指定方式的处理。
try:该代码块中编写可能产生异常的代码。
catch:用来进行某种异常的捕获,实现对捕获到的异常进行处理。
finally代码块
finally:有一些特定的代码无论异常是否发生,都需要执行。另外,因为异常会引发程序跳转,导致有些语句执行不到。而finally就是解决这个问题的,在finally代码块中存放的代码都是一定会被执行的。
异常注意事项
1、运行时异常被抛出可以不处理。即不捕获也不声明抛出。
2、如果父类抛出了多个异常 ,子类覆盖父类方法时,只能抛出相同的异常或者是他的子集。
3、父类方法没有抛出异常,子类覆盖父类该方法时也不可抛出异常。此时子类产生该异常,只能捕获处理,不能声明抛出。
4、当多异常处理时,捕获处理,前边的类不能是后边类的父类
5、 try/catch后可以追加finally代码块,其中的代码一定会被执行,通常用于资源回收。
6、如果 finallyreturn语句,永远返回finally中的结果,避免该情况。
异常类如何定义:
1. 自定义一个编译期异常: 自定义类 并继承于 java.lang.Exception
2. 自定义一个运行时期的异常类:自定义类 并继承于 java.lang.RuntimeException
day06:【线程、线程同步、线程状态】
并发与并行
并行 :指两个或多个事件在同一时刻发生(同时发生)。
并发 :指两个或多个事件在同一个时间段内发生。
线程与进程
进程 :是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。
线程 :进程内部的一个独立执行单元;一个进程可以同时并发的运行多个线程,可以理解为一个进程便相当于一个单 CPU 操作系统,而线程便是这个系统中运行的多个任务。
进程与线程的区别
进程:有独立的内存空间,进程中的数据存放空间(堆空间和栈空间)是独立的,至少有一个线程。
线程:堆空间是共享的,栈空间是独立的,线程消耗的资源比进程小的多。
注意:
1. 因为一个进程中的多个线程是并发运行的,那么从微观角度看也是有先后顺序的,哪个线程执行完全取决于CPU 的调度,程序员是干涉不了的。而这也就造成的多线程的随机性。
2. Java 程序的进程里面至少包含两个线程,主进程也就是 main()方法线程,另外一个是垃圾回收机制线程。每当使用 java 命令执行一个类时,实际上都会启动一个 JVM,每一个 JVM 实际上就是在操作系统中启动了一个线程,java 本身具备了垃圾的收集机制,所以在 Java 运行时至少会启动两个线程。
3. 由于创建一个线程的开销比创建一个进程的开销小的多,那么我们在开发多任务运行的时候,通常考虑创建多线程,而不是创建多进程。
创建线程类Thread
Java使用 java.lang.Thread 类代表线程,所有的线程对象都必须是Thread类或其子类的实例。
构造方法:
public Thread() :分配一个新的线程对象。
public Thread(String name) :分配一个指定名字的新的线程对象。
public Thread(Runnable target) :分配一个带有指定目标新的线程对象。
public Thread(Runnable target,String name) :分配一个带有指定目标新的线程对象并指定名字。
常用方法:
public String getName() :获取当前线程名称。
public void start() :导致此线程开始执行; Java虚拟机调用此线程的run方法。
public void run() :此线程要执行的任务在此处定义代码。
public static void sleep(long millis) :使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。
public static Thread currentThread() :返回对当前方法所处的线程对象。(重点)
创建线程方式一:
1. 定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务,因此把run()方法称为线程执行体。
2. 创建Thread子类的实例,即创建了线程对象
3. 调用线程对象的start()方法来启动该线程
创建线程方式二:采用 java.lang.Runnable,需要重写run方法即可。(线程任务类)
1. 定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
2. 创建Runnable实现类的实例,并以此实例作为Threadtarget来创建Thread对象,该Thread对象才是真正的线程对象。
3. 调用线程对象的start()方法来启动线程。
注意:多线程执行时,在栈内存中,其实每一个执行线程都有一片自己所属的栈内存空间。进行方法的压栈和弹栈。
实际上所有的多线程代码都是通过运行Threadstart()方法来运行的。Runnable对象仅仅作为Thread对象的targetRunnable实现类里包含的run()方法仅作为线程执行体。
而实际的线程对象依然是Thread实例,只是该Thread线程负责执行其targetrun()方法。
Thread Runnable的区别
实现Runnable接口比继承Thread类所具有的优势:
1. 适合多个相同的程序代码的线程去共享同一个资源。
2. 可以避免java中的单继承的局限性。
3. 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和数据独立。
4. 线程池只能放入实现Runablecallable类线程,不能直接放入继承Thread的类。
线程安全:
线程安全问题都是由全局变量及静态变量引起的。若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。
线程同步:
Java中提供了同步机制(synchronized)来解决;
1 . 同步代码块。
2. 同步方法。
3. 锁机制。
同步代码块 synchronized 关键字可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。
格式:
synchronized(同步锁){
     需要同步操作的代码
}
同步锁:
对象的同步锁只是一个概念,可以想象为在对象上标记了一个锁.
1. 锁对象 可以是任意类型。
2. 多个线程对象 要使用同一把锁。
注意:在任何时候,最多允许一个线程拥有同步锁,谁拿到锁就进入代码块,其他的线程只能在外等着。
同步方法 : 使用synchronized修饰的方法,就叫做同步方法,保证A线程执行该方法的时候,其他线程只能在方法外等着。
格式:
public synchronized void method(){
   可能会产生线程安全问题的代码
}
同步方法里的同步锁是谁?
对于非static方法,同步锁就是this
对于static方法,我们使用当前方法所在类的字节码对象(类名.class)
Lock 锁(锁机制):
java.util.concurrent.locks.Lock 机制提供了比synchronized代码块和synchronized方法更广泛的锁定操作,同步代码块/同步方法具有的功能Lock都有,除此之外更强大,更体现面向对象。
Lock接口,要使用它必须使用它的实现类ReentrantLock
Lock锁也称同步锁,加锁与释放锁方法化了,如下:
public void lock() :加同步锁。
public void unlock() :释放同步锁。
线程状态(了解)
六种线程状态:
NEW(新建):线程刚被创建,但是并未启动。
Runnable(可运行):线程可以在java虚拟机中运行的状态,可能正在运行自己代码,也可能没有,这取决于操作系统处理器。
Blocked(锁阻塞):当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状态;当该线程持有锁时,该线程将变成Runnable状态。
Waiting(无限等待):一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态。进入这个状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒。
Timed Waiting(计时等待):同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。这一状态将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有Thread.sleep Object.wait
Teminated(被终止):因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡。
day07:【线程池、Lambda表达式】
等待唤醒机制—线程间通信
概念:多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同。
wait/notify 就是线程间的一种协作机制。
等待唤醒机制就是用于解决线程间通信的问题的,使用到的3个方法的含义如下:
1. wait:线程不再活动,不再参与调度,进入 wait set 中,因此不会浪费 CPU 资源,也不会去竞争锁了,这时的线程状态即是 WAITING。它还要执行一个特别的动作,也即是“通知(notify)”在这个对象上等待的线程从wait set 中释放出来,重新进入到调度队列(ready queue)中
2. notify:则选取所通知对象的 wait set 中的一个线程释放
3. notifyAll:则释放所通知对象的 wait set 上的全部线程。
注意:被唤醒的等待线程如果能获取锁,线程就从 WAITING 状态变成 RUNNABLE 状态;
否则,从 wait set 出来,又进入 entry set,线程就从 WAITING 状态又变成 BLOCKED 状态。
调用waitnotify方法需要注意的细节
1. wait方法与notify方法必须要由同一个锁对象调用。因为:对应的锁对象可以通过notify唤醒使用同一个锁对象调用的wait方法后的线程。
2. wait方法与notify方法是属于Object类的方法的。因为:锁对象可以是任意对象,而任意对象的所属类都是继承了Object类的。
3. wait方法与notify方法必须要在同步代码块或者是同步函数中使用。因为:必须要通过锁对象调用这2个方法。
线程池:
线程池:其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。
理利用线程池能够带来三个好处:
1. 降低资源消耗。减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
2. 提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。
3. 提高线程的可管理性。可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)
线程池的使用
Java里面线程池的顶级接口是 java.util.concurrent.Executor ,但是严格意义上讲 Executor 并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是java.util.concurrent.ExecutorService
Executors类中有个创建线程池的方法如下:
public static ExecutorService newFixedThreadPool(int nThreads) :返回线程池对象。(创建的是有界线程池,也就是池中的线程个数可以指定最大数量)
获取到了一个线程池ExecutorService 对象,那么怎么使用呢,在这里定义了一个使用线程池对象的方法如下:
public Future<?> submit(Runnable task) :获取线程池中的某一个线程对象,并执行
Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用。
使用线程池中线程对象的步骤:
1. 创建线程池对象。
2. 创建Runnable接口子类对象。(task)
3. 提交Runnable接口子类对象。(take task)
4. 关闭线程池(一般不做)
Lambda表达式
Runnable 接口只有一个 run 方法的定义:
public abstract void run() ;
即制定了一种做事情的方案(其实就是一个函数):
无参数 :不需要任何条件即可执行该方案。
无返回值 :该方案不产生任何结果。
代码块 (方法体):该方案的具体执行步骤。
同样的语义体现在 Lambda 语法中,要更加简单:
() > System.out.println("多线程任务执行!")
前面的一对小括号即 run 方法的参数(无),代表不需要任何条件;
中间的一个箭头代表将前面的参数传递给后面的代码;
后面的输出语句即业务逻辑代码。
Lambda 标准格式:(参数类型 参数名称) > { 代码语句 }
格式说明:
小括号内的语法与传统方法参数列表一致:无参数则留空;多个参数则用逗号分隔。
- > 是新引入的语法格式,代表指向动作。
大括号内的语法与传统方法体要求基本一致。
省略规则
Lambda标准格式的基础上,使用省略写法的规则为:
1. 小括号内参数的类型可以省略;
2. 如果小括号内有且仅有一个参,则小括号可以省略;
3. 如果大括号内有且仅有一个语句,则无论是否有返回值,都可以省略大括号、return关键字及语句分号。
Lambda 的使用前提
使用Lambda必须具有接口,且要求接口中有且仅有一个抽象方法。
备注:有且仅有一个抽象方法的接口,称为“函数式接口”。
无论是JDK内置的 Runnable Comparator 接口还是自定义的接口,只有当接口中的抽象方法存在且唯一时,才可以使用Lambda
2. 使用Lambda必须具有上下文推断。也就是方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例。


3 个回复

倒序浏览
回复 使用道具 举报
可以
回复 使用道具 举报
奈斯啊兄dei
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马