堆栈花园的多线程学习备忘录——线程管理

一、线程组

1. 线程组的基本介绍

  • 线程组就类似于在计算机中用文件夹管理文件,所以我们也可以用线程组来管理线程。在线程组中定义一组相似(相关)的线程,在线程组中同样也可以定义子线程组,就像文件夹里还有子文件夹一样。
  • Thread类有多个构造方法,允许我们在创建线程时,指定一个线程组,如果没有指定,那么这个线程就属于父线程所在的线程组,JVM在创建main线程时,会给它指定一个线程组,那么所有线程都一定会有一个线程组和它相关,线程有一个方法getThreadGroup()方法返回它的线程组。
  • 线程组的设计是处于安全的考虑,区分不同的Applet,然而线程组没能实现,它是有缺陷的,所以我们现在一般不用了。现在我们一般把一组相关线程放到集合或者数组里,或者就直接取不同的线程名字来区分。

2. 设置守护线程组

  • 守护线程是为其他线程提供服务的,当JVM中只有守护线程时,守护线程会自动销毁。
  • 调用线程组的setDaemon(true)可以吧线程组设置为守护线程组,当守护线程组中没有任何活动线程时,守护线程组会自动销毁。注意:如果一个线程组是守护线程组,该线程组里面的线程可以是守护线程也可以不是守护线程。
二、捕获线程的执行异常
  • 在线程的run方法中,如果存在编译时异常,我们肯定要进行捕获处理。但是如果我们需要获得run方法中的运行时异常,可以通过回调UncaughtExcpetionHandler接口,获得哪个线程出现了运行时异常。
  • 在Thread类中有关处理运行时异常的方法有:
    (1)getDefaultUncaughtExceptionHandler()获得全局的"UncaughtExceptionHandler"对象,这是一个静态方法。
    (2)getUncaughtExceptionHandler()获得当前线程的"UncaughtExceptionHandler"对象。
    (3)setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)设置全局的UncaughtExceptionHandler
    (4)setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)设置当前线程的UncaughtExceptionHandler
  • 当线程运行过程中出现异常,JVM会调用Thread类中的private void dispatchUncaughtException(Throwable e)方法,该方法会调用getUncaughtExceptionHandler().uncaughtException(this, e);,如果当前设置了UncaughtExceptionHandler,就调用他自己的uncaughtException,否贼就调用当前线程组的UncaughtExceptionHandler里的uncaughtException方法。如果当前线程组也没设置回调接口,就把异常栈信息定向到System.err里。
  • 那么综上所述,我们要获得线程中出现的运行时异常,就需要设置线程的UncaughtExceptionHandler

演示代码:

public static void main(String[] args) {
        //设置线程全局回调接口,new的是一个接口,我们用匿名内部类的方式实现它
        Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                //t参数就是我们需要获得异常的线程,e就是这个线程里发出的异常
                System.out.println(t.getName()+" :产生了异常,该线程产生了 :"+e.getMessage()+" 的异常");
            }
        });
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"开始运行");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(0/0);//在2秒钟后产生算术异常
            }
        },"t1");
        t1.start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"开始运行");
                Object object = null;
                object.toString();//立马产生空指针异常
            }
        },"t2").start();

    }

运行结果:在这里插入图片描述
这种异常处理的方式还是比较常用的,特别是异步处理的代码。

三、线程池

1. 什么是线程池

  • 我们可以用new Thread(...).start();这种形式开启一个线程,当run方法结束,线程对象会被GC回收。
  • 在开发中,可能有很多很多个线程来支持整个项目,当线程很多的时候,反而会耗尽CPU资源,如果不对线程进行控制和管理,会影响到程序的性能。
  • 线程开销主要包括:创建和启动线程的开销;线程回收的开销;线程调度的开销;线程数量受限于CPU本身。
  • 线程池就是有效使用线程的一种常用方式。线程池内部会预先创建一定数量的工作线程,客户端发来的任务直接作为一个对象提交给线程池,然后线程池将这些任务缓存到工作队列里,线程池中的工作线程不断地从队列里取任务,执行。
    在这里插入图片描述2. JDK为线程池提供的API
  • JDK提供了一套Executor框架,可以帮助我们使用线程池。在java.util.concurrent包中。
  • Executor接口中,有一个子接口ExecutorService,这个子接口还有一个子接口ScheduledExecutorService,在超级接口Executor中有一个方法execute(Runnable command)
  • ExecutorService接口中,增加了submit(Runnable task)方法,就是提交任务的,同时返回一个Future,以及增加了shutdown()方法,可以关闭线程池。同时这个接口有一个实现类ThreadPoolExecutor
  • ScheduledExecutorService接口中,又增加了schedule(Runnable command, long delay, TimeUnit unit方法,可以延时执行。同时这个接口有一个实现类ScheduledThreadPoolExecutor
  • 在java.util.concurrent中还有一个类叫Executors,和接口ExecutorService是依赖关系,其中有newCachedThreadPool() newFixedThreadPool(int nThreads) newSingleThreadExecutor()方法,他们都会返回一个ExecutorService

演示代码:

 public static void main(String[] args) {
        //创建有5个工作线程的线程池
        ExecutorService executor = Executors.newFixedThreadPool(5);

        //向线程池中提交10个任务,这10个任务放到了阻塞队列中。
        for(int i = 0;i < 10;i++){
            executor.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "开始执行任务,当前时间"+System.currentTimeMillis());
                    try {
                        Thread.sleep(2000);//模拟执行任务的时间
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }

运行结果:在这里插入图片描述在创建的线程池中的五个线程,各自从阻塞队列中拿到任务,进行执行,若干时间执行完毕后,继续从阻塞队列中拿取任务执行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值