java学习笔记(4)基本概念之异常、多线程常

异常:是在运行时期发生的不正常情况。。


在java中用类的形式对不正常情况进行了描述和封装对象。

描述不正常的情况的类,就称为异常类。

以前正常流程代码和问题处理代码相结合,
现在将正常流程代码和问题处理代码分离。提高阅读性.


其实异常就是java通过面向对象的思想将问题封装成了对象.

用异常类对其进行描述。
不同的问题用不同的类进行具体的描述。 比如角标越界。空指针等等。

问题很多,意味着描述的类也很多,
将其共性进行向上抽取,形成了异常体系。

最终问题(不正常情况)就分成了两大类。
Throwable:无论是error,还是异常,问题,问题发生就应该可以抛出,让调用者知道并处理。
            //该体系的特点就在于Throwable及其所有的子类都具有可抛性。
            可抛性到底指的是什么呢?怎么体现可抛性呢?
            其实是通过两个关键字来体现的。
            throws throw ,凡是可以被这两个关键字所操作的类和对象都具备可抛性.
    |--1,一般不可处理的。Error
            特点:是由jvm抛出的严重性的问题。
                 这种问题发生一般不针对性处理。直接修改程序
    |--2,可以处理的。Exception


该体系的特点:
    子类的后缀名都是用其父类名作为后缀,阅读性很想。


对于角标是整数不存在,可以用角标越界表示,
对于负数为角标的情况,准备用负数角标异常来表示。

负数角标这种异常在java中并没有定义过。
那就按照java异常的创建思想,面向对象,将负数角标进行自定义描述。并封装成对象。

这种自定义的问题描述成为自定义异常。

注意:如果让一个类称为异常类,必须要继承异常体系,因为只有称为异常体系的子类才有资格具备可抛性。
    才可以被两个关键字所操作,throws throw




异常的分类:
1,编译时被检测异常:只要是Exception和其子类都是,除了特殊子类RuntimeException体系。
        这种问题一旦出现,希望在编译时就进行检测,让这种问题有对应的处理方式。
        这样的问题都可以针对性的处理。


2,编译时不检测异常(运行时异常):就是Exception中的RuntimeException和其子类。
        这种问题的发生,无法让功能继续,运算无法进行,更多是因为调用者的原因导致的而或者引发了内部状态的改变导致的。
        那么这种问题一般不处理,直接编译通过,在运行时,让调用者调用时的程序强制停止,让调用者对代码进行修正。



所以自定义异常时,要么继承Exception。要么继承RuntimeException。


throws 和throw的区别。

1,throws使用在函数上。
   throw使用在函数内。
2,throws抛出的是异常类,可以抛出多个,用逗号隔开。
   throw抛出的是异常对象。
   
   
   
 异常处理的捕捉形式:
这是可以对异常进行针对性处理的方式。

具体格式是:

try
{
    //需要被检测异常的代码。
}
catch(异常类 变量)//该变量用于接收发生的异常对象
{
    //处理异常的代码。
}
finally
{
    //一定会被执行的代码。
}





异常处理的原则:
1,函数内容如果抛出需要检测的异常,那么函数上必须要声明。
    否则必须在函数内用trycatch捕捉,否则编译失败。
    
2,如果调用到了声明异常的函数,要么trycatch要么throws,否则编译失败。

3,什么时候catch,什么时候throws 呢?
    功能内容可以解决,用catch。
    解决不了,用throws告诉调用者,由调用者解决 。

4,一个功能如果抛出了多个异常,那么调用时,必须有对应多个catch进行针对性的处理。
    内部又几个需要检测的异常,就抛几个异常,抛出几个,就catch几个。


    
    
    
    
    
    

异常的注意事项:

1,子类在覆盖父类方法时,父类的方法如果抛出了异常,
那么子类的方法只能抛出父类的异常或者该异常的子类。

2,如果父类抛出多个异常,那么子类只能抛出父类异常的子集。



简单说:子类覆盖父类只能抛出父类的异常或者子类或者子集。

注意:如果父类的方法没有抛出异常,那么子类覆盖时绝对不能抛,就只能try .









总结:
包与包之间的类进行访问,被访问的包中的类必须是public的,被访问的包中的类的方法也必须是public的。

          public        protected        default          private
同一类中    ok             ok              ok           ok
同一包中    ok             ok              ok           
子类中      ok             ok              
不同包中    ok









进程:正在进行中的程序(直译).
        
线程:就是进程中一个负责程序执行的控制单元(执行路径)
一个进程中可以多执行路径,称之为多线程。

一个进程中至少要有一个线程。

开启多个线程是为了同时运行多部分代码。

每一个线程都有自己运行的内容。这个内容可以称为线程要执行的任务。

多线程好处:解决了多部分同时运行的问题。

多线程的弊端:线程太多回到效率的降低。


其实应用程序的执行都是cpu在做着快速的切换完成的。这个切换是随机的。


JVM启动时就启动了多个线程,至少有两个线程可以分析的出来。

1,执行main函数的线程,
        该线程的任务代码都定义在main函数中。

2,负责垃圾回收的线程。



如何创建一个线程呢?

创建线程方式一:继承Thread类。

步骤:
1,定义一个类继承Thread类。
2,覆盖Thread类中的run方法。
3,直接创建Thread的子类对象创建线程。
4,调用start方法开启线程并调用线程的任务run方法执行。



可以通过Thread的getName获取线程的名称 Thread-编号(从0开始)

主线程的名字就是main。


创建线程的目的是为了开启一条执行路径,去运行指定的代码和其他代码实现同时运行。
        
        而运行的指定代码就是这个执行路径的任务。

        jvm创建的主线程的任务都定义在了主函数中。

        而自定义的线程它的任务在哪儿呢?
        Thread类用于描述线程,线程是需要任务的。所以Thread类也对任务的描述。
        这个任务就通过Thread类中的run方法来体现。也就是说,run方法就是封装自定义线程运行任务的函数。
        
        run方法中定义就是线程要运行的任务代码。

        开启线程是为了运行指定代码,所以只有继承Thread类,并复写run方法。
        将运行的代码定义在run方法中即可。
        
同步函数的使用的锁是this;

同步函数和同步代码块的区别:
同步函数的锁是固定的this。

同步代码块的锁是任意的对象。

建议使用同步代码块。


创建线程的第一种方式:继承Thread类。

创建线程的第二种方式:实现Runnable接口。

1,定义类实现Runnable接口。
2,覆盖接口中的run方法,将线程的任务代码封装到run方法中。
3,通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递。
    为什么?因为线程的任务都封装在Runnable接口子类对象的run方法中。
    所以要在线程对象创建时就必须明确要运行的任务。

4,调用线程对象的start方法开启线程。


实现Runnable接口的好处:
1,将线程的任务从线程的子类中分离出来,进行了单独的封装。
    按照面向对象的思想将任务的封装成对象。
2,避免了java单继承的局限性。

所以,创建线程的第二种方式较为常用。



线程安全问题产生的原因:

1,多个线程在操作共享的数据。
2,操作共享数据的线程代码有多条。

当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算。
就会导致线程安全问题的产生。


解决思路;
就是将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候,
其他线程时不可以参与运算的。
必须要当前线程把这些代码都执行完毕后,其他线程才可以参与运算。

在java中,用同步代码块就可以解决这个问题。

同步代码块的格式:
synchronized(对象)
{
    需要被同步的代码 ;
}

同步的好处:解决了线程的安全问题。


同步的弊端:相对降低了效率,因为同步外的线程的都会判断同步锁。


同步的前提:同步中必须有多个线程并使用同一个锁。


notify:只能唤醒一个线程,如果本方唤醒了本方,没有意义。而且while判断标记+notify会导致死锁。
notifyAll解决了本方线程一定会唤醒对方线程的问题。


jdk1.5以后将同步和锁封装成了对象。
并将操作锁的隐式方式定义到了该对象中,
将隐式动作变成了显示动作。

Lock接口: 出现替代了同步代码块或者同步函数。将同步的隐式锁操作变成现实锁操作。
同时更为灵活。可以一个锁上加上多组监视器。
lock():获取锁。
unlock():释放锁,通常需要定义finally代码块中。


Condition接口:出现替代了Object中的wait notify notifyAll方法。
            将这些监视器方法单独进行了封装,变成Condition监视器对象。
            可以任意锁进行组合。
await();
signal();
signalAll();



等待/唤醒机制。

涉及的方法:

1,wait(): 让线程处于冻结状态,被wait的线程会被存储到线程池中。
2,notify():唤醒线程池中一个线程(任意).
3,notifyAll():唤醒线程池中的所有线程。

这些方法都必须定义在同步中。
因为这些方法是用于操作线程状态的方法。
必须要明确到底操作的是哪个锁上的线程。


为什么操作线程的方法wait notify notifyAll定义在了Object类中?

因为这些方法是监视器的方法。监视器其实就是锁。
锁可以是任意的对象,任意的对象调用的方式一定定义在Object类中






停止线程:
1,stop方法。

2,run方法结束。

怎么控制线程的任务结束呢?
任务中都会有循环结构,只要控制住循环就可以结束任务。

控制循环通常就用定义标记来完成。

但是如果线程处于了冻结状态,无法读取标记。如何结束呢?

可以使用interrupt()方法将线程从冻结状态强制恢复到运行状态中来,让线程具备cpu的执行资格。

当时强制动作会发生了InterruptedException,记得要处理



1,wait可以指定时间也可以不指定。
   sleep必须指定时间。

2,在同步中时,对cpu的执行权和锁的处理不同。
    wait:释放执行权,释放锁。
    sleep:释放执行权,不释放锁。













  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值