java异常框架||java多线程
1 运行时异常与一般异常有何异同
答:。java编译器要求方法必须声明抛出可能发生的非运行时异常,但是并不要求必须声明抛出未被捕获的运行时异常。运行时异常表示虚拟机的通常操作中可能遇到的异常,是一种常见运行错误。运行时期异常一旦发生,需要程序人员修改源代码.(这些异常通常是由于逻辑错误引起的)一般异常(编译时异常):必须进行处理的异常,如果不处理,程序就不能编译通过,可以通过try..catch处理或用throws声明继续抛给上层调用方法处理
运行时异常,我们可以不处理。当出现这样的异常时,总是由虚拟机接管。 RuntimeException 体系包括错误的类型转换、数组越界访问和试图访问空指针等等。假如出现 RuntimeException,那么一定是程序员的错误。
一般异常:定义方法时必须声明所有可能会抛出的异常;在调用这个方法时,可以捕获它的异常,还能把它的exception传递下去;
2 Java中的异常处理机制的简单原理和应用
答:所有异常的根类是Throwable,Throwable又派生了两个子类:Error和Exception,Exception包括运行时期异常和编译期异常。Error错误通常没有具体的处理方式,程序将会结束运行。java为系统异常和普通异常提供了不同的解决方案(捕获、往上抛出 ),编译器强制普通异常必须try..[catch..finaly]处理或throws声明继续抛给上层调用方法处理。所以普通异常为checked异常,而系统异常可以处理也可以不处理。编译器不强制用try..catch或throws声明,所以系统异常成为uncheckde异常。
①java异常处理用到了多态的概念,如果在异常处理过程中,先捕获了基类,然后在捕获子类,那么捕获子类的代码块永远不会执行.因此,在进行异常捕获时,正确的写法是先捕获子类,再捕获父类的异常信息
②异常能处理就处理,不能处理就抛出.对于一般异常,如果不能进行行之有效的处理,最好转换成运行时异常抛出
3 JAVA语言如何进行异常处理,关键字:throws,throw,try,catch,finally分别代表什么意义?在try块中可以抛出异常吗?
答:异常处理一共有两种方式:第一种不处理异常,让其往上抛。第二种就是try。。catch。。。finaly捕获异常处理。
throws是可以获取方法体中一个至多个异常,throws是方法可能抛出异常的声明。
throw是抛出一个异常对象
try是将会发生异常的语句括起来,从而进行异常的处理,
catch是如果有异常就会执行他里面的语句
finally不论是否有异常都会进行执行的语句。。return;也不能让finally停止执行,可以关闭jvm主线程停止执行finally(System.exit(1))
try中可以抛出一个异常:通过throw new XXXException("xxxxx");
4 编写程序举例抛出一个空指针异常、类型转换异常、数组索引越界异常、字符串索引越界异常
package Demo1; public class Student { int score; int age; String name; Computer computer; public void study() { System.out.println("studying..."); } public static void main(String[] args) { Student student = new Student(); student.score = 90; int[] arr = {1, 2, 3}; String str = "hello"; student = null; try { System.out.println(str.charAt(5));//字符串索引越界异常 System.out.println(arr[5]);//数组小标越界异常 int x = Integer.parseInt("ok");//类型转换异常 System.out.println(student.score);//程序出现空指针异常 } catch (NullPointerException e) { System.out.println("程序出现空指针异常!!!"); } catch (NumberFormatException e) { System.out.println("类型转换异常"); } catch (ArrayIndexOutOfBoundsException e) { System.out.println("数组小标越界异常"); } catch (StringIndexOutOfBoundsException e) { System.out.println("字符串索引越界异常"); } } }
线程创建方式一
继承Thread
类
-
创建一个继承于Thread类的子类
-
重写Thread类的run() --> 将此线程执行的操作声明在run()中
-
创建Thread类的子类的对象
-
通过此对象调用start()
注意点:
-
如果自己手动调用run()方法,那么就只是普通方法,没有启动多线程模式。
-
run()方法由JVM调用,什么时候调用,执行的过程控制都有操作系统的CPU 调度决定。
-
想要启动多线程,必须调用start方法。
-
一个线程对象只能调用一次start()方法启动,如果重复调用了,则将抛出以上 的异常
IllegalThreadStateException
。
课堂练习
创建两个分线程,让其中一个线程输出1-100之间的偶数,另一个线程输出1-100之间的奇数。
Thread
类常用方法
-
void start(): 启动线程,并执行对象的run()方法
-
run(): 线程在被调度时执行的操作
-
String getName(): 返回线程的名称
-
void setName(String name):设置该线程名称
-
static Thread currentThread(): 返回当前线程。在Thread子类中就是this,通常用于主线程和Runnable实现类
-
static void yield():线程让步
-
暂停当前正在执行的线程,把执行机会让给优先级相同或更高的线程
-
若队列中没有同优先级的线程,忽略此方法
-
-
join() :当某个程序执行流中调用其他线程的 join() 方法时,调用线程将 被阻塞,直到 join() 方法加入的 join 线程执行完为止低优先级的线程也可以获得执行
-
static void sleep(long millis):(指定时间:毫秒)
-
令当前活动线程在指定时间段内放弃对CPU控制,使其他线程有机会被执行,时间到后 重排队。
-
抛出
InterruptedException
异常
-
-
stop(): 强制线程生命期结束,不推荐使用
-
boolean isAlive():返回boolean,判断线程是否还活着
线程调度(优先级)
Java的调度方法
-
同优先级线程组成先进先出队列(先到先服务),使用时间片策略
-
对高优先级,使用优先调度的抢占式策略
线程的优先级等级
-
MAX_PRIORITY:10
-
MIN _PRIORITY:1
-
NORM_PRIORITY:5
涉及的方法
-
getPriority()
:返回线程优先值 -
setPriority(int newPriority)
:改变线程的优先级
说明
-
线程创建时继承父线程的优先级
-
低优先级只是获得调度的概率低,并非一定是在高优先级线程之后才被调用
线程创建方式二
实现Runnable
接口
-
创建一个实现了Runnable接口的类
-
实现类去实现Runnable中的抽象方法:run()
-
创建实现类的对象
-
将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
-
通过Thread类的对象调用start()
两种方式比较
区别
-
继承Thread:线程代码存放Thread子类run方法中。
-
实现Runnable:线程代码存在接口的子类的run方法。
实现方式的好处
-
避免了单继承的局限性
-
多个线程可以共享同一个接口实现类的对象,非常适合多个相同线程来处理同一份资源。
Synchronized
关键字
使用
synchronized
关键字可以解决线程同步问题
使用方式
-
同步代码块:
synchronized (对象){
//需要被同步的代码;
}
-
同步方法
-
synchronized还可以放在方法声明中,表示整个方法为同步方法。
-
public synchronized void show (String name){
//需要被同步的代码;
}
关于锁
的概念
-
同步锁机制:
-
在《Thinking in Java》中,是这么说的:对于并发工作,你需要某种方式来防止两个任务访问相同的资源(其实就是共享资源竞争)。 防止这种冲突的方法就是当资源被一个任务使用时,在其上加锁。第一个访问某项资源的任务必须锁定这项资源,使其他任务在其被解锁之前,就无法访问它了,而在其被解锁 之时,另一个任务就可以锁定并使用它了。
-
-
synchronized的锁是什么?
-
任意对象都可以作为同步锁。所有对象都自动含有单一的锁(监视器)。
-
同步方法的锁:静态方法(类名.class)、非静态方法(this)
-
同步代码块:自己指定,很多时候也是指定为this或类名.class
-
-
注意:
-
必须确保使用同一个资源的多个线程共用一把锁,这个非常重要,否则就无法保证共享资源的安全
-
一个线程类中的所有静态方法共用同一把锁(类名.class),所有非静态方法共用同一把锁(this),同步代码块(指定需谨慎)
-
同步的范围
-
如何找问题,即代码是否存在线程安全?(非常重要)
-
明确哪些代码是多线程运行的代码 run()
-
明确多个线程是否有共享数据
-
明确多线程运行代码中是否有多条语句操作共享数据
-
-
如何解决呢?(非常重要)
-
对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行。
-
即所有操作共享数据的这些语句都要放在同步范围中
-
-
切记:
-
范围太小:没锁住所有有安全问题的代码
-
范围太大:没发挥多线程的功能。
-
Lock
方式
-
从JDK 5.0开始,Java提供了更强大的线程同步机制——通过显式定义同步锁对象来实现同步。同步锁使用Lock对象充当。
-
java.util.concurrent.locks.Lock
接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象。 -
ReentrantLock
类实现了 Lock ,它拥有与 synchronized 相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁、释放锁。
同步代码块中涉及到同步监视器和共享数据,谈谈你对他们的理解:
答:监视器一般我们说成同步锁。任意一个类的实例对象都可以充当这锁。但是为了保证多线程数据是共享操作的,锁一定要同一把(同一个对象)才行。不过同步锁比较麻烦,不能锁多了逻辑块,也不能锁少了,不然达不到目的。同步方法默认锁的是this,当前调用者自己。extends实现多线程有时候比较鸡肋。
锁对象我们一般使用两个:
-
this
-
同一操做目标类的字节码文件:XXX.class || this.getClas()(不可靠)
除了使用Synchronized
关键字,从JDK 5.0开始,Java提供了更强大的线程同步机制——通过显式定义同步锁对象来实现同步。同步锁使用Lock对象充当。Lock是显式锁(手动开启和关闭锁,别忘记关闭锁)。一般使用Lock接口的实现类:ReentrantLock ;
共享数据:多个线程共同操作的数据,即为共享数据。它可以被任意一个线程所操作而改变。
创建多线程有哪几种方式
答:
-
继承Thread类实现多线程
-
实现Runnable接口
-
实现Callable接口
-
线程池
通讯方法
-
wait()与notify()和notifyAll()
-
wait():令当前线程挂起并放弃CPU、同步资源并等待,使别的线程可访问并修改共享资源,而当前线程排队等候其他线程调用notify()或notifyAll()方法唤醒,唤醒后等待重新获得对监视器的所有权后才能继续执行。
-
notify():唤醒正在排队等待同步资源的线程中优先级最高者结束等待
-
notifyAll ():唤醒正在排队等待资源的所有线程结束等待.
-
-
这三个方法只有在synchronized方法或synchronized代码块中才能使用,否则会报 java.lang.IllegalMonitorStateException异常。
-
因为这三个方法必须有锁对象调用,而任意对象都可以作为synchronized的同步锁,因此这三个方法只能在Object类中声明。
wait()方法
-
在当前线程中调用方法: 对象名.wait()
-
使当前线程进入等待(某对象)状态 ,直到另一线程对该对象发出 notify (或notifyAll) 为止。
-
调用方法的必要条件:当前线程必须具有对该对象的监控权(加锁)
-
调用此方法后,当前线程将释放对象监控权 ,然后进入等待
-
在当前线程被notify后,要重新获得监控权,然后从断点处继续代码的执行。
notify()/notifyAll()
-
在当前线程中调用方法: 对象名.notify()
-
功能:唤醒等待该对象监控权的一个/所有线程。
-
调用方法的必要条件:当前线程必须具有对该对象的监控权(加锁)
线程实现的其他方式
-
与使用Runnable相比, Callable功能更强大些
-
相比run()方法,可以有返回值
-
方法可以抛出异常
-
支持泛型的返回值
-
需要借助
FutureTask
类,比如获取返回结果
-
-
Future接口
-
可以对具体Runnable、Callable任务的执行结果进行取消、查询是否完成、获取结果等。
-
FutrueTask是Futrue接口的唯一的实现类
-
FutureTask 同时实现了Runnable, Future接口。它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值
-
import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; /** * 创建线程的方式三:实现Callable接口。 --- JDK 5.0新增 * * * 如何理解实现Callable接口的方式创建多线程比实现Runnable接口创建多线程方式强大? * 1. call()可以有返回值的。 * 2. call()可以抛出异常,被外面的操作捕获,获取异常的信息 * 3. Callable是支持泛型的 * */ //1.创建一个实现Callable的实现类 class NumThread implements Callable{ //2.实现call方法,将此线程需要执行的操作声明在call()中 @Override public Object call() throws Exception { int sum = 0; for (int i = 1; i <= 100; i++) { if(i % 2 == 0){ System.out.println(i); sum += i; } } return sum; } } public class ThreadNew { public static void main(String[] args) { //3.创建Callable接口实现类的对象 NumThread numThread = new NumThread(); //4.将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象 FutureTask futureTask = new FutureTask(numThread); //5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start() new Thread(futureTask).start(); try { //6.获取Callable中call方法的返回值 //get()返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值。 Object sum = futureTask.get(); System.out.println("总和为:" + sum); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } }
使用线程池
-
背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。
-
思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。
-
好处:
-
提高响应速度(减少了创建新线程的时间)
-
降低资源消耗(重复利用线程池中线程,不需要每次都创建)
-
便于线程管理
-
corePoolSize:核心池的大小
-
maximumPoolSize:最大线程数
-
keepAliveTime:线程没有任务时最多保持多长时间后会终止
-
-
-
JDK 5.0起提供了线程池相关API:ExecutorService 和 Executors
-
ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor
-
void execute(Runnable command) :执行任务/命令,没有返回值,一般用来执行Runnable
-
<T> Future<T> submit(Callable<T> task):执行任务,有返回值,一般又来执行Callable
-
void shutdown() :关闭连接池
-
-
Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
-
Executors.newCachedThreadPool():创建一个可根据需要创建新线程的线程池
-
Executors.newFixedThreadPool(n); 创建一个可重用固定线程数的线程池
-
Executors.newSingleThreadExecutor() :创建一个只有一个线程的线程池
-
Executors.newScheduledThreadPool(n):创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。
-
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; /** * 创建线程的方式四:使用线程池 * * 好处: * 1.提高响应速度(减少了创建新线程的时间) * 2.降低资源消耗(重复利用线程池中线程,不需要每次都创建) * 3.便于线程管理 * corePoolSize:核心池的大小 * maximumPoolSize:最大线程数 * keepAliveTime:线程没有任务时最多保持多长时间后会终止 * * * 面试题:创建多线程有几种方式?四种! */ class NumberThread implements Runnable{ @Override public void run() { for(int i = 0;i <= 100;i++){ if(i % 2 == 0){ System.out.println(Thread.currentThread().getName() + ": " + i); } } } } class NumberThread1 implements Runnable{ @Override public void run() { for(int i = 0;i <= 100;i++){ if(i % 2 != 0){ System.out.println(Thread.currentThread().getName() + ": " + i); } } } } public class ThreadPool { public static void main(String[] args) { //1. 提供指定线程数量的线程池 ExecutorService service = Executors.newFixedThreadPool(10); ThreadPoolExecutor service1 = (ThreadPoolExecutor) service; //设置线程池的属性 // System.out.println(service.getClass()); // service1.setCorePoolSize(15); // service1.setKeepAliveTime(); //2.执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象 service.execute(new NumberThread());//适合适用于Runnable service.execute(new NumberThread1());//适合适用于Runnable // service.submit(Callable callable);//适合使用于Callable //3.关闭连接池 service.shutdown(); } }
个性签名:独学而无友,则孤陋而寡闻。做一个灵魂有趣的人!
如果觉得这篇文章对你有小小的帮助的话,记得在右下角点个“推荐”哦,博主在此感谢!
Java入门到入坟
万水千山总是情,打赏一分行不行,所以如果你心情还比较高兴,也是可以扫码打赏博主,哈哈哈(っ•̀ω•́)っ✎⁾⁾!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南