一.异常(Exception)
1.Exception:异常
编译时期异常和运行时期异常(RuntimeException):
程序在运行过程中出现问题(代码书写不严谨),
只要不是RuntimeException的子类都是属于编译时期异常;
2.编译时期异常:在程序,运行前需要检查的.
举例:在生活中 "长途旅行之前,检查你的车胎情况"
运行时期异常:在程序,程序代码逻辑问题(代码不严谨)
举例:在生活中 "no zuo no die"
3.异常的处理两种方式:
(1)标准格式:try...catch...finally
变形格式
try{
//可能出现问题的代码
}catch(异常类名 变量名){
//处理异常
}
-----------------------------------------------------------------------------------------
try{
//可能出现问题的代码
}catch(异常类名 变量名){
//处理异常1
}catch(异常类名 变量名){
//处理异常2
}
-----------------------------------------------------------------------------------------
//多线程:jdk5以后:Lock:接口 (锁:可重复入的互斥锁)
try{
//可能出现问题的代码
}finally{
//释放资源(系统资源)
}
(2)throws:抛出
4.使用try...catch去处理多个异常
jdk7以后提供的
try{
可能出现问题的代码
}catch(异常类名1 | 异常类名2 | 异常类名3 ...变量名){ //异常类名必须为同级别
处理异常
}
5.编译时期异常和运行时期异常的区别?
(1) 运行时期异常:
一般程序员逻辑结构不严谨导致的问题,调用者可以进行显示处理(try...catch.../throws)
也可以不进行显示处理,通过逻辑语句进行处理(2)编译时期异常:调用者必须显示处理,不处理,编译通过不了,程序运行不了
面试题一
throws和throw的区别?
(1)共同点
两者在抛出异常时,抛出异常的方法并不负责处理,顾名思义,只管抛出,由调用者负责处理。
(2)区别
a.throws用于方法头,表示的只是异常的申明,
throw用于方法内部,抛出的是异常对象。
b.throws可以一次性抛出多个异常,
throw只能抛出一个异常.
c.throws抛出异常时,它的上级(调用者)也要申明抛出异常或者捕获,不然编译报错。
throw的话,可以不申明或不捕获(这是非常不负责任的方式)但编译器不会报错。
面试题二
如果在某个方法中捕获异常,但是该方法有返回值类型,
如果在catch语句出现return语句,finally代码 还会执行吗?
如果会执行,在return前还是在后
public class Test { public static void main(String[] args) { int num = getNum(10) ;// i=10 System.out.println(num); } private static int getNum(int i) { try{ i = 20 ; //i = 20 ; System.out.println(i/0); //除数为0 }catch (ArithmeticException e){ i = 30 ; //i =30 return i ; // return i = return 30 :已经在catch语句形成返回的路径 返回结果就是30 }finally { //finally代码一定会执行,除非jvm退出了 i = 40 ; // i = 40 // return i ; } return i; //30 } }
finally会执行,但是现在这个代码,在catch语句已经形成返回路径,它会记录最终返回就是30;finally是去释放资源用的,很少牵扯业务代码;都会执行的,除非jvm退出
二.线程
1.线程
线程:属于程序中执行的最小的单元(进程中的一条任务线)
进程:能够调用的系统资源的独立单位
一个进程由多个线程组成,多个线程---->线程组(ThreadGroup)
线程依赖于进程
单线程:程序在执行过程中,始终只有一条路径
多线程:程序在执行过程中,有多条执行路径
并发:在一个时间点同时----->和cpu性能有关系(cpu的逻辑核数)
并行:在一个时间段内同时
2.Thread类的构造方法:
Thread(String name):创建线程类对象,设置名称
3.Thread类的成员方法
public final String getName():获取线程名称
public final void setName(String name):设置线程名称
4.线程的优先级
Thread类中静态常量字段(成员变量field)
public static final int MAX_PRIORITY 10 最大优先级
public static final int MIN_PRIORITY 1 最小优先级
public static final int NORM_PRIORITY 5 默认优先级
public final void setPriority(int newPriority):设置线程的优先级
public final int getPriority():获取优先级
优先级越大的:抢占CPU的执行权越大
优先级小的:抢占CPU的执行权越小
默认优先级:随机性大一些
5.多线程的意义?
答:多线程的特点:具有随机性
多个线程在抢占CPU的执行权
面试题一
jvm是多线程吗?
是多线程:
至少有两条线程
用户线程main,以及创建对象的时候,当对象使用完毕,
需要被垃圾回收器回收jvm针对没有更多的引用对象,
开启一条垃圾回收线程!
面试题二
线程的状态有几种?生命周期:从线程的创建,就绪,执行,结束
答:NEW(新建状态)
RUNNABLE(执行状态)
BLOCKED(阻塞状态)
WAITING(死死等待)
TIMED_WAITING(超时等待)
TERMINATED(死亡状态)
6.线程的创建方式第一种的步骤?(只有弊端,几乎没有任何优点)
(1)将一个类声明为Thread的子类;
(2)这个子类应该重写Thread类的run方法;
(3)创建当前的子类对象,分别启动线程start();
public class MyThread extends Thread {
//重写Thread类的方法
@Override
public void run() {
//run方法里面:一般情况耗时的操作
for(int x = 0 ; x < 200 ; x ++){
System.out.println(x);
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
//3)创建Thread类的子类对象
MyThread my1 = new MyThread() ;//第一个线程对象
MyThread my2 = new MyThread() ; //第二个线程对象
my1.start();//start():有jvm调用底层run方法,出现并发执行
my2.start();
}
}
7.线程的创建方式第二种的步骤?
(1)自定义类实现Runnable接口
(2)重写Runnable接口的run方法
(3)创建当前类对象,作为资源共享类
分别创建Thread类对象,将资源类对象作为参数传递
(4)分别启动线程;
public class MyRunnable implements Runnable {
@Override
public void run() {
//耗时的操作
for(int x = 0 ; x < 100 ; x ++){
//public static Thread currentThread()
System.out.println(Thread.currentThread().getName()+":"+x);
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
//可以分配类的实例(创建类的实例)
MyRunnable my = new MyRunnable() ; //资源类:被多线程共享//具体类new 具体类
//创建两个线程类对象
Thread t1 = new Thread(my,"张俊杰") ;
Thread t2 = new Thread(my,"高圆圆") ;
//分别启动线程
t1.start();
t2.start();
}
}
8.检验多线程安全问题的标准;
1)是否是多线程环境-----------------------------是-----------------不能更改,使用多线程实现
2)是否存在共享数据-----------------------------是------------------必须要有共享数据
3)是否存在多条语句对共享数据的操作-----是-------------------解决点
解决----Java提供同步机制:同步代码块 将多条对共享数据包裹起来
synchronized(锁对象){
将多条对共享数据包裹起来
}
锁对象:必须要多个线程使用的同一个锁对象,而不是分别自己的锁对象
以电影院3个窗口共买100张电影票为例
public class SellTicket implements Runnable {
//成员变量;100张票
public static int tickests = 100 ;
//创建一个锁对象:
public Object obj = new Object() ;
//t1,t2,t3
@Override
public void run() {
//模拟一直票
while(true){
//t1,t2,t3
//解决方案:
//将多条语句对共享数据的操作包裹起来
//synchronized (new Object()){ //锁对象 :三个线程分别使用自己的锁
//必须为是同一个锁对象
synchronized (obj){
//模拟网络延迟
//判断
if(tickests>0){
try {
Thread.sleep(100); //单位为毫秒数
} catch (InterruptedException e) {
e.printStackTrace();
}
//输出窗口信息
System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickests--)+"张票");
}
}
}
}
}
public class SellTicketTest {
public static void main(String[] args) {
//创建资源类对象SellTicket
SellTicket st = new SellTicket() ;
//创建三个线程类对象
Thread t1 = new Thread(st,"窗口1") ;
Thread t2 = new Thread(st,"窗口2") ;
Thread t3 = new Thread(st,"窗口3") ;
//分别启动线程
t1.start();
t2.start();
t3.start();
}
}
9.什么是同步方法?
如果一个方法的方法体的第一句话就是同步代码块
可以将synchronized关键字提取到方法声明上,跟在权限修饰符的后面
权限修饰符 synchronized 返回值类型 方法名(形式列表){ //非静态的同步方法
业务逻辑...
}
面试题:
wait()方法/notify()方法 ---- 也可以称为"同步" --->等待唤醒机制
线程等待/线程唤醒 这些方法为什么不定义在Thread类中呢,而是定义Object类中呢?
它和锁对象有关系,而锁对象:可以任何的java类对象,-----Object(顶层父类)
syncronized(锁对象){
锁对象.wait()
//锁对象.notify()
}
10.线程安全问题:可以通过同步方法或者是同步代码块去解决,但是执行过程中就可能出现死锁问题
死锁问题:
(使用同步机制解决线程安全) 线程和线程之间出现了互相等待的情况!
解决方案:
多个线程之间的通信:必须使用的是一个资源类对象,而不能是每一个线程在使用自己的资源类对象!
使用生成者和消费者模式思想去解决,前提条件:生成者线程和消费者线程 必须操作的同一个资源类对象!
10.1
使用生成者和消费者思想模式---解决线程死锁问题
1)StuffBun包子类属性
包含包子的名称name
包子的大小type
2)生产者资源类 SetBun 产生包子
3)消费者资源类 GetBun 使用包子
4)ThreadDemo:main 用户线程
按照上面的方式:模拟生产者产生数据,消费者使用数据出现问题 null---null
生产资源类中和消费者资源类中所操作的包子对象不是同一个对象!
可以将包子通过生产资源类或者消费者资源类 通过构造方法传递
优化1:
加入while循环,模拟包子一直生产和一直消费!
出现问题:数据紊乱:加入同步代码块给每一个资源类中都加入解决! 将多条语句对共享数据的操作包起来;
问题:
消费者资源类所在的消费者线程中,每次输出一大片的内容:
线程的执行随机性----线程抢占CPU的执行权,一点点时间片执行很多次-------------------------------------------------------------------------------------------------------------------------
优化2:
想出现依次打印
肉包子---大包子
菜包子---小包子
...wait()+notify()--->实现同步机制(并且同时信号法:将死锁问题解决)
包子类
public class StuffBun { //成员变量不私有化 String name ;//包子的类型(肉包子,菜包子) String bunType ;//大包子/小包子 //定义标记:表示是否存在包子数据 boolean flag ; //默认false,没有数据 }
消费者资源类
public class GetBun implements Runnable { //声明包子类的变量stu private StuffBun stu ; public GetBun( StuffBun stu){ this.stu = stu ; } @Override public void run() { //模拟要使用数据 // StuffBun stb = new StuffBun() ; //不断使用数据 while(true){ synchronized (stu){ //如果当前消费资源类中存在包子数据,先等待消费使用完毕数据 if(!stu.flag){ //等待使用完毕数据 try { stu.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(stu.name+"---"+stu.bunType); //改变信号值 //如果包子消费完毕 stu.flag = false ; //唤醒对方线程(生产者资源类,别等了,产生数据) stu.notify(); } } } }
生产者资源类
public class SetBun implements Runnable { //声明这个包子类 private StuffBun stu ; public SetBun(StuffBun stu){ this.stu = stu ; } //定义一个统计变量 int x = 0 ; @Override public void run() { while(true){ synchronized (stu){ //如果当前生产者没有语句,需要等待生成产生数据 if(stu.flag){ //锁对象调用wait发那个发 try { stu.wait();//释放锁对象... } catch (InterruptedException e) { e.printStackTrace(); } } if(x % 2 == 0){//t1 stu.name = "肉包子" ; stu.bunType = "大包子"; }else{ stu.name = "菜包子" ; stu.bunType = "小包子" ; } //如果现在有数据了 //改变信号 stu.flag = true ;//有数据类 //通知(唤醒)消费者线程,赶紧使用数据 stu.notify(); //唤醒对方线程 } x ++ ; } }
测试类
public class ThreadDemo { public static void main(String[] args) { //创建一个包子对象 StuffBun sbu = new StuffBun() ; //同一个对象 //创建生产资源类对象 SetBun sb = new SetBun(sbu) ; //消费者资源类对象 GetBun gb = new GetBun(sbu) ; //创建线程了对象 Thread t1 = new Thread(sb) ;//生产者资源类所在的生产者线程 Thread t2 = new Thread(gb) ;//消费者资源类所在的消费者线程 t1.start(); t2.start(); } }
11. Lock
JDK5以后提供java.util.current.locks.Lock:提供比syncrhonized方法(/同步代码块)更具体的锁定操作
多个线程并发访问,抢占共享资源数据,通过lock实现多个线程对某个共享资源进行独占访问,不会安全问题
Lock是一个接口
void lock()获取锁
void unlock() 试图释放锁
提供跟具体的子实现类:ReentrantLock实现电影卖票:三个窗口出售100张票 (线程的创建方式2)
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class SellTicket implements Runnable { //定义100张票 private static int tickets = 100 ; //创建一个锁对象 Lock lock = new ReentrantLock() ; @Override public void run() { //模拟一只有票 while(true){ //通过锁对象--->获取锁 lock.lock(); //try...catch...finaly:捕获异常 //使用try..finally try{ //判断 if(tickets>0){ //睡眠100毫秒 try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickets--)+"张票"); }else{ break ; } }finally { //释放锁 lock.unlock(); } } } }
public class LockDemo { public static void main(String[] args) { //创建共享资源类对象 SellTicket st = new SellTicket() ; //创建三个线程类对象 Thread t1 = new Thread(st,"窗口1") ; Thread t2 = new Thread(st,"窗口2") ; Thread t3 = new Thread(st,"窗口3") ; //启动线程 t1.start(); t2.start(); t3.start(); } }
12.线程组 ThreadGroup(了解即可)
线程组代表一组线程。 此外,线程组还可以包括其他线程组
线程组: 将线程可以都添加一组中,方便管理, 线程启动完毕之后,线程终止之后,不会将这个线程对象在内存中重复利用
13.线程池
线程池 属于 "池"化技术 ;
特点:在内存中创建一个固定可重用的线程数,当前线程执行完毕终止了,不会被回收掉,再次回到线程池中,等待下一次利用;
弊端: 维护成本大;
ExecutorService---接口
(1)Exceutors
<1>创建一个固定的可重用的线程数,返回值就线程池对象
public static ExecutorService newFixedThreadPool(int nThreads)
(2)ExecutorService
<1>提交队列任务(多个线程并发执行)
<T> Future<T> submit(Callable<T> task)
<2>提交值返回任务以执行,并返回代表任务待处理结果的Future.
Future<?> submit(Runnable task)
submit的返回值:异步计算的结果,如果不做计算,无须返回结果
MyCallable
import java.util.concurrent.Callable; public class MyCallable implements Callable { @Override public Object call() throws Exception { for(int x = 0 ; x < 100 ; x++){ System.out.println(Thread.currentThread().getName()+":"+x); } return null; } }
ThreadPoolDemo
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadPoolDemo { public static void main(String[] args) { //通过工厂类创建线程池对象 ExecutorService threadPool = Executors.newFixedThreadPool(2); threadPool.submit(new MyCallable()) ; threadPool.submit(new MyCallable()) ; //void shutdown()关闭线程池 threadPool.shutdown(); } }