35.Weekend 5 Summary(异常和线程)

一.异常(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();


    }
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值