1.异常
根类Throwable体系:java
- Error:严重错误,程序自身已经不能处理的问题,出现的严重错误程序终止运行
- Exception:编译期异常,这种异常是强制咱们使用catch捕获处理或throws抛出给调用者。你遇到这种异常必须进行catch或throws,若是不处理,编译器会报错。
- RuntimeExeption:Exception的子类,运行时异常,这种异常咱们不须要处理,彻底由虚拟机接管。
- NullPointerException:RuntimeExeption的子类,空指针异常
- RuntimeExeption:Exception的子类,运行时异常,这种异常咱们不须要处理,彻底由虚拟机接管。
异常处理的五个关键字:try,catch,finally,throw,throws数据库
(1)抛出异常 throw:
语法:throw new 异常类名(参数)数组
例子:throw new NullPointerException(“要访问的数组不存在”)安全
注意:多线程
- throw关键字必须放在方法的内部
- throw关键字后边new的对象必须是Exception或Exception子类对象
- throw关键字抛出指定的异常对象,咱们就必须处理这个异常对象
- throw关键字后边建立的是RuntimeException或RuntimeException的子类对象,咱们能够不处理默认交给JVM处理(打印异常对象,中断程序)
- throw关键字后边建立的是编译异常,咱们处理这个异常,要么throws,要么try...catch
Objects的非空判断:并发
Objects.requireNonNull(obj,message),用来判断obj是否为空,为空则抛出异常,信息为messageide
(2)throws关键字:异常处理的第一种方式,交给别人处理
做用:将异常抛出给方法的调用者处理,最终交给JVM处理-->中断处理工具
使用格式:在方法声明时使用测试
修饰符 返回值类型 方法名(参数列表) throws 异常1,异常2...{ throw new 异常1("xxx"); throw new 异常2("xxx"); ... }
注意:ui
- 必须写在方法声明处
- 声明的异常必须是Exception或Exception子类对象
- 方法内部抛出多个异常,声明处必须也要声明多个异常,若是抛出父异常也抛出子异常则只要声明父异常便可
- 咱们必须处理声明的异常
- 要么交给方法调用者处理,最终交给JVM
- 要么使用try...catch本身处理异常
(3)如何获取异常信息
Throwable中定义了一些查看异常的方法:
- public String getMessage():获取异常描述信息
- public string toString():获取异常的类型和异常描述信息
- public void printStackTrace():打印异常的跟踪栈信息并输出到控制台
(4)try,catch,finally
语法:
try{ 可能出现异常的代码 }catch(异常类1 变量名){ 异常处理的逻辑 } ... catch(异常类n 变量名){ }finally{ 不管是否出现异常都会执行 }
注意:
- finally不能单独使用,必须和try一块儿使用
- finally通常用于资源释放
- 若是finally中有return则永远返回finally中的结果,咱们须要避免出现return。
- 当一个catch捕获来处理异常后就不会调用其余catch处理异常了
- 多个catch捕获异常,咱们先进行子异常捕获处理,若是没有子异常咱们就进行父异常捕获处理
(5)异常注意事项
- 多个异常分别处理:多个try..catch 处理
- 多个异常一次捕获,屡次处理:一个try多个catch处理,子异常在前,父异常在后
- 多个异常一次捕获,一次处理:一个try...catch,异常为全部异常的父类或自己
- 子父类异常:
- 若是父类抛出多个异常,子类重写父类方法时,抛出和父类相同的异常或者是父类异常的子类或者不抛出异常
- 父类方法没有抛出异常,子类重写父类方法时也可不抛出异常,此时子类产生异常,只能捕获异常,不能声明抛出
1 class Father{ 2 public void show01() throws NullPointerException,ClassCastException{} 3 public void show02() throws IndexOutOfBoundsException{} 4 public void show03() throws IndexOutOfBoundsException{} 5 public void show04() {} 6 } 7 8 class Son extends Father{ 9 //子类重写父类方法时,抛出和父类相同的异常 10 public void show01() throws NullPointerException,ClassCastException{} 11 //子类重写父类方法时,抛出父类异常的子类 12 public void show02() throws IndexOutOfBoundsException{} 13 //子类重写父类方法时,不抛出异常 14 public void show03() throws IndexOutOfBoundsException{} 15 //子类重写父类方法时,父类没有抛出异常,子类本身处理异常 16 public void show04() { 17 try { 18 throw new Exception("出现异常"); 19 } catch (Exception e) { 20 e.printStackTrace(); 21 } 22 } 23 }
2.自定义异常类
- 继承Exception处理方式:
- 第一种:抛出异常给调用者,须要声明
- 第二种:本身处理
- 继承RuntimeException处理方式:
- 直接抛出,不用声明直接交给JVM处理
1 public class demo02 { 2 3 4 //抛出异常给调用者处理,须要声明 5 public static void testException1() throws TestException { 6 System.out.println("这是testException1"); 7 throw new TestException("testException1"); 8 } 9 10 11 //抛出异常本身处理 12 public static void testException2() { 13 System.out.println("这是testException2"); 14 try { 15 throw new TestException("testException1"); 16 } catch (TestException e) { 17 e.printStackTrace(); 18 return ; //用于结束方法 19 } 20 21 } 22 23 //运行时异常不用处理和声明,交给JVM处理,最终中断处理 24 public static void testRuntimeException(){ 25 System.out.println("这是testRuntimeException"); 26 throw new TestRuntimeException("testRuntimeException"); 27 } 28 29 public static void main(String[] args) throws TestException { 30 31 demo02.testException1(); 32 demo02.testException2(); 33 demo02.testRuntimeException(); 34 35 } 36 }
TestException
1 public class TestException extends Exception { 2 3 public TestException() { 4 super(); 5 } 6 7 public TestException(String message) { 8 super(message); 9 } 10 }
TestRuntimeException
1 public class TestRuntimeException extends RuntimeException{ 2 3 public TestRuntimeException() { 4 super(); 5 } 6 7 public TestRuntimeException(String message) { 8 super(message); 9 } 10 }
补充:Java常见异常
1. RuntimeException子类:
序号 | 异常名称 | 异常描述 |
---|---|---|
1 | java.lang.ArrayIndexOutOfBoundsException | 数组索引越界异常。当对数组的索引值为负数或大于等于数组大小时抛出。 |
2 | java.lang.ArithmeticException | 算术条件异常。譬如:整数除零等。 |
3 | java.lang.SecurityException | 安全性异常 |
4 | java.lang.IllegalArgumentException | 非法参数异常 |
5 | java.lang.ArrayStoreException | 数组中包含不兼容的值抛出的异常 |
6 | java.lang.NegativeArraySizeException | 数组长度为负异常 |
7 | java.lang.NullPointerException | 空指针异常。当应用试图在要求使用对象的地方使用了null时,抛出该异常。譬如:调用null对象的实例方法、访问null对象的属性、计算null对象的长度、使用throw语句抛出null等等。 |
2.IOException
序号 | 异常名称 | 异常描述 |
---|---|---|
1 | IOException | 操做输入流和输出流时可能出现的异常 |
2 | EOFException | 文件已结束异常 |
3 | FileNotFoundException | 文件未找到异常 |
3. 其余
序号 | 异常名称 | 异常描述 |
---|---|---|
1 | ClassCastException | 类型转换异常类 |
2 | ArrayStoreException | 数组中包含不兼容的值抛出的异常 |
3 | SQLException | 操做数据库异常类 |
4 | NoSuchFieldException | 字段未找到异常 |
5 | NoSuchMethodException | 方法未找到抛出的异常 |
6 | NumberFormatException | 字符串转换为数字抛出的异常 |
7 | StringIndexOutOfBoundsException | 字符串索引超出范围抛出的异常 |
8 | IllegalAccessException | 不容许访问某类异常 |
9 | InstantiationException | 当应用程序试图使用Class类中的newInstance()方法建立 一个类的实例,而指定的类对象没法被实例化时,抛出该异常 |
10 | java.lang.ClassNotFoundException | 找不到类异常。当应用试图根据字符串形式的类名构造类,而在遍历CLASSPAH以后找不到对应名称的class文件时,抛出该异常。 |
3.多线程
(1)Thread类:
构造方法:
- public Thread():分配一个新的线程对象
- public Thread(String name):分配一个指定名字的新的线程对象
- public Thread(Runnable target):分配一个带有指定目标新的线程对象
- public Thread(Runnable target,String name):分配一个带有指定目标新的线程对象并指定名字
经常使用方法:
- public String getName():获取当前线程名称
- public void setName():设置当前线程名称
- public void start():让线程开始执行,Java虚拟机调用该线程的run方法
- public void run():该线程要执行的任务在此处定义代码
- public static void sleep(long millis):是当前正在执行的线程以指定的毫秒数暂停
- public static Thread currentThread():返回对当前正在执行的线程对象的引用
MyThread类:
1 public class MyThread extends Thread { 2 3 public MyThread() { 4 } 5 6 public MyThread(String name) { 7 super(name); 8 } 9 10 @Override 11 public void run() { 12 13 System.out.println(Thread.currentThread().getName()); 14 } 15 }
demo01类:
1 public class demo01 { 2 3 4 public static void main(String[] args) { 5 6 //1.修改线程名称方法一 7 MyThread thread1 = new MyThread(); 8 thread1.setName("thread1"); 9 thread1.start(); //thread1 10 11 //修改线程名称方法二 12 MyThread thread2 = new MyThread("thread2"); 13 thread2.start(); //thread2 14 15 16 for (int i = 0; i < 60; i++) { 17 System.out.println(i); 18 19 //2.使当前线程睡眠1秒执行一次 20 try { 21 Thread.sleep(1000); 22 } catch (InterruptedException e) { 23 e.printStackTrace(); 24 } 25 } 26 27 28 } 29 }
(2)Runnable接口:
实现步骤:
- 建立一个Runnable接口的实现类
- 在实现类中重写Runnable接口的run方法,设置线程任务
- 建立一个Runnable接口的实现类对象
- 建立Thread类对象,构造方法中传递Runnable接口实现类对象
- 调用start方法,开启新线程执行run方法
RunnableImpl类:
1 public class RunnableImpl implements Runnable { 2 @Override 3 public void run() { 4 5 for (int i = 0; i < 60; i++) { 6 System.out.println(Thread.currentThread().getName() + ":" + i); 7 } 8 } 9 }
demo02类:
1 public class demo02 { 2 3 public static void main(String[] args) { 4 5 RunnableImpl runnable = new RunnableImpl(); 6 new Thread(runnable).start(); 7 8 for (int i = 0; i < 60; i++) { 9 10 System.out.println(Thread.currentThread().getName() + ":" + i); 11 12 } 13 } 14 }
(3)实现Runnable接口和继承Thread类比较
实现Runnable接口优势:
- 避免了单继承的局限性
- 加强了程序的扩展性,下降了程序的耦合性:把设置线程任务和开启线程分离
(4)匿名内部类实现线程的建立
优势:简化代码
1 public class demo03 { 2 3 public static void main(String[] args) { 4 5 //匿名Thread 6 new Thread() { 7 @Override 8 public void run() { 9 10 for (int i = 0; i < 20; i++) { 11 System.out.println("Thread:" + i); 12 } 13 } 14 }.start(); 15 16 //匿名Runnable 17 new Thread(new Runnable() { 18 @Override 19 public void run() { 20 for (int i = 0; i < 20; i++) { 21 System.out.println("Runnable:" + i); 22 } 23 } 24 }).start(); 25 } 26 }
4.并发和并行
并发:多个任务在同一时间段执行,任务交替执行
并行:多个任务在同一时刻执行,任务同时执行
线程:进程中的一个执行单元
进程:内存中运行的一个应用程序
(1)线程安全
1.同步代码块:
synchronized (同步锁){ 须要同步操做的代码(访问了共享数据的代码) }
保证了线程安全,可是频繁的判断锁,释放锁和获取锁致使程序的效率下降
RunableImpl类:this指RunnableImpl建立的对象
1 public class RunnableImpl implements Runnable { 2 3 private int num = 100; 4 5 @Override 6 public void run() { 7 8 while (true){ 9 10 synchronized (this){ 11 if (num > 0){ 12 try { 13 Thread.sleep(10); 14 } catch (InterruptedException e) { 15 e.printStackTrace(); 16 } 17 System.out.println(Thread.currentThread().getName() + "得到当前数字为:" + num); 18 num--; 19 } 20 } 21 } 22 23 } 24 }
demo04类:
1 public class demo04 { 2 3 public static void main(String[] args) { 4 5 RunnableImpl runnable = new RunnableImpl(); 6 new Thread(runnable).start(); 7 new Thread(runnable).start(); 8 new Thread(runnable).start(); 9 10 11 } 12 }
2.同步方法:
public synchronized void method(){ 可能会产生线程安全的代码 }
保证了线程安全,可是频繁的判断锁,释放锁和获取锁致使程序的效率下降
RunnableImpl类:this指RunnableImpl建立的对象
1 public class RunnableImpl implements Runnable { 2 3 private int num = 100; 4 5 @Override 6 public void run() { 7 8 while (true){ 9 10 fun(); 11 } 12 13 } 14 15 public synchronized void fun(){ 16 if (num > 0){ 17 try { 18 Thread.sleep(10); 19 } catch (InterruptedException e) { 20 e.printStackTrace(); 21 } 22 System.out.println(Thread.currentThread().getName() + "得到当前数字为:" + num); 23 num--; 24 } 25 } 26 }
demo05:
1 public class demo05 { 2 3 public static void main(String[] args) { 4 5 RunnableImpl runnable = new RunnableImpl(); 6 new Thread(runnable).start(); 7 new Thread(runnable).start(); 8 new Thread(runnable).start(); 9 10 } 11 }
3.静态同步方法:
public static synchronized void method(){ 可能会产生线程安全的代码,只能调用静态属性 }
RunnableImpl:静态方法的锁对象是本类的class属性-->class文件对象
1 public class RunnableImpl implements Runnable { 2 3 private static int num = 100; //静态属性 4 5 @Override 6 public void run() { 7 8 while (true){ 9 10 fun(); 11 } 12 13 } 14 15 //静态方法 16 public static synchronized void fun(){ 17 if (num > 0){ 18 try { 19 Thread.sleep(10); 20 } catch (InterruptedException e) { 21 e.printStackTrace(); 22 } 23 System.out.println(Thread.currentThread().getName() + "得到当前数字为:" + num); 24 num--; 25 } 26 } 27 }
另外一种写法:
1 //静态方法 2 public static void fun() { 3 synchronized (RunnableImpl.class) { 4 if (num > 0) { 5 try { 6 Thread.sleep(10); 7 } catch (InterruptedException e) { 8 e.printStackTrace(); 9 } 10 System.out.println(Thread.currentThread().getName() + "得到当前数字为:" + num); 11 num--; 12 } 13 } 14 15 }
demo06:
1 public class demo06 { 2 3 public static void main(String[] args) { 4 5 RunnableImpl runnable = new RunnableImpl(); 6 new Thread(runnable).start(); 7 new Thread(runnable).start(); 8 new Thread(runnable).start(); 9 10 } 11 }
(2)Lock锁
java.util.concurrent.locks.Lock 机制提供了比 synchronized 代码块和方法更加普遍的锁定操做。
Lock锁也称为同步锁
- Lock是一个接口在1.5版本后出现
- public void lock():加同步锁
- public void unlock():释放同步锁
- ReentrantLock实现了Lock接口
1 public class RunnableImpl implements Runnable { 2 3 private int num = 100; //静态属性 4 5 private final ReentrantLock lock = new ReentrantLock(); 6 7 @Override 8 public void run() { 9 10 while (true) { 11 12 //加锁 13 lock.lock(); 14 15 if (num > 0) { 16 try { 17 Thread.sleep(10); 18 System.out.println(Thread.currentThread().getName() + "得到当前数字为:" + num); 19 num--; 20 } catch (InterruptedException e) { 21 e.printStackTrace(); 22 } finally { 23 24 //释放锁 25 lock.unlock(); 26 } 27 } 28 } 29 } 30 }
(3)线程的状态
- 初始(NEW):新建立了一个线程对象,但尚未调用start()方法。
- 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。线程对象建立后,其余线程(好比main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在得到CPU时间片后变为运行中状态(running)。
- 阻塞(BLOCKED):表示线程阻塞于锁。
- 等待(WAITING):进入该状态的线程须要等待其余线程作出一些特定动做(通知或中断)。
- 计时等待(TIMED_WAITING):该状态不一样于WAITING,它能够在指定的时间后自行返回。
- 死亡(TERMINATED):表示该线程已经执行完毕。
(4)线程经常使用方法:
- void wait():当前线程进入等待状态
- void wait(long timeout):当前线程进入等待状态后,等待指定时间后自动唤醒
- void notify():唤醒等待的单个线程,随机唤醒一个
- void notifyAll():唤醒等待的全部线程
(5)线程通讯案例:单生产者和单消费者问题
产品类:
1 public class Product { 2 3 private String name; 4 private Deque<String> deque; 5 6 7 public Product(String name, Deque<String> deque) { 8 this.name = name; 9 this.deque = deque; 10 } 11 12 public Deque<String> getDeque() { 13 return deque; 14 } 15 16 public void setDeque(Deque<String> deque) { 17 this.deque = deque; 18 } 19 20 public String getName() { 21 return name; 22 } 23 24 public void setName(String name) { 25 this.name = name; 26 } 27 28 29 @Override 30 public String toString() { 31 return "Product{" + 32 "name='" + name + '\'' + 33 ", deque=" + deque + 34 '}'; 35 } 36 }
单生产者Producer类:
1 public class Producer implements Runnable { 2 3 private Product product; 4 private int productID = 1; 5 6 public Producer(Product product, int productID) { 7 this.product = product; 8 this.productID = productID; 9 } 10 11 public Producer(Product product) { 12 this.product = product; 13 } 14 15 @Override 16 public void run() { 17 18 while (true) { 19 synchronized (product) { 20 21 //当前产品数量 22 int num = product.getDeque().size(); 23 24 if (num >= 10) { 25 26 try { 27 product.wait(); 28 } catch (InterruptedException e) { 29 e.printStackTrace(); 30 } 31 } 32 33 num = product.getDeque().size(); 34 35 String name = productID + "号产品"; 36 product.getDeque().add(name); 37 productID++; 38 39 System.out.println("+++++当前库存" + num + "件产品,正在生成产品名为" + name + "的产品"); 40 41 42 try { 43 Thread.sleep(100); 44 } catch (InterruptedException e) { 45 e.printStackTrace(); 46 } 47 48 49 50 System.out.println("++++++++++++++++已经生产好了产品名为" + name + "的产品,当前库存" + (num + 1) + "件产品"); 51 product.notify(); 52 53 } 54 } 55 } 56 }
单消费者Consumer类:
1 public class Consumer implements Runnable { 2 3 private String name; 4 private Product product; 5 6 public Consumer(String name, Product product) { 7 this.name = name; 8 this.product = product; 9 } 10 11 @Override 12 public void run() { 13 14 while (true) { 15 16 synchronized (product) { 17 18 19 int num = product.getDeque().size(); 20 21 if (num <= 0) { 22 try { 23 product.wait(); 24 } catch (InterruptedException e) { 25 e.printStackTrace(); 26 } 27 } 28 29 num = product.getDeque().size(); 30 31 String name = product.getDeque().getFirst(); 32 33 System.out.println("-----消费者" + this.name + "正在消费产品名为" + name + "的产品"); 34 35 product.getDeque().remove(); 36 37 try { 38 Thread.sleep(50); 39 } catch (InterruptedException e) { 40 e.printStackTrace(); 41 } 42 43 System.out.println("--------------消费者" + this.name + "已经消费了产品名为" + name + "的产品,剩余库存" + (num - 1)); 44 product.notify(); 45 46 47 } 48 } 49 } 50 }
测试代码:
1 public class day01 { 2 3 public static void main(String[] args) { 4 5 Deque<String> deque = new ArrayDeque<>(); 6 Product product = new Product("产品类别1",deque); 7 8 new Thread(new Producer(product)).start(); 9 new Thread(new Consumer("Consumer1", product)).start(); 10 11 12 } 13 }
5.线程池
- JDK1.5以后提供的线程池
- 顶层接口java.util.concurrent.Executor做为执行线程的工具而不是真正的线程池。
- java.util.concurrent.ExecutorService为线程池接口继承Executor接口。
- java.util.concurrent.Executors为线程池的工厂类。
(1)线程池经常使用方法
- Executors工厂类中:
- public static ExecutorService new FixedThreadPool(int nThreads):建立一个能够重用的固定线程数的线程池
- ExecutorService线程池接口:
-
Future<?> submit(Runnable task):提交一个Runnable任务执行。
- void shutdown():销毁线程池。
-
(2)执行步骤:
- 使用Executors工厂类调用FixedThreadPool方法来建立一个线程池。
- 编写一个实现Runnable接口的实现类,重写run方法。
- 调用ExecutorService类方法submit传入Runnable的实现类并执行。
- 最终销毁线程池ExecutorService类方法shutdown,现实中不必定须要。
RunnableImpl:
1 public class RunnableImpl implements Runnable { 2 3 private int num = 2; 4 @Override 5 public void run() { 6 while (num-- > 0) { 7 8 System.out.println(Thread.currentThread().getName()); 9 try { 10 Thread.sleep(100); 11 } catch (InterruptedException e) { 12 e.printStackTrace(); 13 } 14 } 15 } 16 }
main:
1 public class demo01 { 2 3 public static void main(String[] args) { 4 5 //建立 6 ExecutorService es = Executors.newFixedThreadPool(10); 7 8 //执行 9 es.submit(new RunnableImpl()); //pool-1-thread-1 10 es.submit(new RunnableImpl()); //pool-1-thread-2 11 es.submit(new RunnableImpl()); //pool-1-thread-3 12 es.submit(new RunnableImpl()); //pool-1-thread-4 13 14 //销毁 15 es.shutdown(); 16 17 } 18 }