多线程三(线程组和线程池)

线程组和线程池

一. 线程组

1. 线程组介绍及使用

Java使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,Java允许直接对线程组进行控制。对线程组的控制相当于控制这批线程。

在默认情况下,子线程和创建它的父线程同属于一个线程组。

一旦线程假如某个线程组之后,该线程将一直属于该线程组,知道该线程死亡,线程运行途中不能改变它所属的线程组。

Thread提供了不同构造器设置新创建的线程属于哪个线程组。提供getThreadGroup()方法返回该线程所属的线程组对象。

ThreadGroup类提供了如下两个构造器创建实例。

  • ThreadGroup(String name):以指定的线程组名字来创建新的线程组
  • ThreadGroup(ThreadGroup parent,String name):以指定的名字、指定的父线程组创建一个新线程组

Java程序不允许改线程组名字,通过getName()方法获取线程组名字。

ThreadGroup类提供了如下常用的方法

  • int activeCount():返回此线程组中活动的线程数目
  • interrupt():中断此线程组中的所有线程
  • isDaemon():判断该线程组是否是后台线程组
  • setDaemon(boolean daemon):把该线程组设置成后台线程组。
  • setMaxPriority(int pri):设置线程组的最高优先级。

2.线程组和异常处理机制

从Java 5开始,Java加强了线程的异常处理,如果线程执行过程中抛出了一个未处理异常,JVM在结束该线程之前会自动查找是否有对应的Thread.UncaughtExceptionHandler对象,如果找到该处理器对象,则会调用该对象的uncaughtException(Thread t,Throwable e)方法来处理该异常。

Thread类提供了两个方法设置异常处理器。

  • static setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh):为该线程类的所有线程实例设置默认的异常处理器
  • setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh):为指定的线程实例设置异常处理器

ThreadGroup类实现了Thread.UncaughtExceptionHandler接口,所以每个线程所属的线程组将会作为默认的异常处理器。

如果线程执行过程中抛出了一个未处理异常,JVM在结束该线程之前会自动查找是否有对应的Thread.UncaughtExceptionHandler对象,如果找到该处理器对象,则会调用该对象的uncaughtException(Thread t,Throwable e)方法来处理该异常;否则,JVM会调用该线程所属的线程组对象的uncaughtException()方法来处理该异常。

线程组处理异常的流程如下:

  • 如果该线程组有父线程组,则调父线程组的uncaughtException()方法来处理该异常。
  • 如果该线线程实例所属的线程类有默认的异常处理器,那么调用该异常处理器来处理异常
  • 如果该对象是ThreadDeath对象,则不做任何处理;否则,将异常跟踪栈的信息打印到System.err错误输出流,并结束该线程。

下面主程序设置了异常处理器。

package com.gdut.thread;

class MyExHandler implements Thread.UncaughtExceptionHandler{
    @Override
    public void uncaughtException(Thread t, Throwable e) {
        System.out.println(t+"线程出现了异常"+e);
    }
}
public class ExHandler {
    public static void main(String[] args) {
        Thread.currentThread().setUncaughtExceptionHandler(new MyExHandler());
        int a=5/0;
        System.out.println("程序正常结束");
    }
}

输出:Thread[main,5,main]线程出现了异常java.lang.ArithmeticException: / by zero

二. 线程池

系统启动一个新线程的成本是非常高的,因为它涉及与操作系统交互。当程序中需要创建大量生存期很短暂的线程时,应该考虑使用线程池来提高系统性能。

与数据库连接池类似的是,线程池在系统启动时即创建大量空闲的线程,当序将一个Runnable对象或Callable对象创给线程池,线程池就会启动一个线程来执行他们的run()或call()方法,当run()或call()方法执行结束后,该线程并不会死亡,而是再次返回线程池中称为空闲状态,等待执行下一个Runnable对象的run()或call()方法。

除此之外,使用线程池可以有效地控制系统中并发线程的数量,当系统中包含大量并发线程时,会导致系统性能剧烈下降,甚至导致JVM崩溃。

2.1 Java 8改进的线程池

2.2 Java 8增强的线池

为了充分利用多CPU的优势、多核CPU的性能优势。可以考多个小任务,把小任务放到多个处理器核心上并行执行;当多个小任务执行完成之后,再将这些执行结果合并起来即可。Java 7提供了ForkJoinPool来支持这个功能。

ForkJoinPool是ExecutorService的实现类,因此是一种特殊的线程池。提供了如下两个常用的构造器

  • ForkJoinPool(int parallelism):创建一个包含parallelism个并行线程的ForkJoinPool.
  • ForkJoinPool():以Runtime.availableProssesors()方法的返回值作为paralelism参数来创建ForkJoinPool.

Java 8进一步拓展了ForkJoinPool的功能,Java 8增加了通用池功能。ForkJoinPool通过如下两个方法提供通用池功能。

  • ForkJoinPool commonPool():该方法返回一个通用池,通用池的状态不会受shutdown()或shutdownNow()方法的影响。
  • int getCommonPoolParallelism():该方法返回通用池的并行级别。

创建了通用池ForkJoinPool实例之后,就可调用ForkJoinPool的submit(ForkJoinTask task)或invoke(ForkJoinTask task)方法来执行指定任务了。其中,ForkJoinTask代表一个并行,合并的任务。

ForkJoinTask是一个抽象类,它还有两个抽象子类:RecursiveAction和recursiveTask。其中RecursiveAction代表没有返回值的任务,RecursiveTask代表有返回值的任务。

下面程序将一个大任务(打印0~500)的数值分成多个小任务,并将任务交给ForkJoinPool来执行。

package com.gdut.thread.threadPool;

import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;
import java.util.concurrent.TimeUnit;

class PrintTask extends RecursiveAction{

    private static final int THRESHOLD = 50;
    private int start;
    private int end;
    public PrintTask(int start,int end) {
      this.start = start;
      this.end = end;
    }

    @Override
    protected void compute() {
        if(end-start<THRESHOLD){
            for (int i = start; i <end ; i++) {
                System.out.println(Thread.currentThread().getName()+"的i值"+i);
            }
        }else{
            //当end与start的差大于THRESHOLD时,即要打印的数超过50时,将大任务分成两个小任务
            int middle = (end+start)/2;
            PrintTask left = new PrintTask(start,middle);
            PrintTask right = new PrintTask(middle,end);
            left.fork();
            right.fork();
        }
    }
}
public class ForkJoinPoolTest{
    public static void main(String[] args) throws InterruptedException{
        ForkJoinPool pool = new ForkJoinPool();
        pool.submit(new PrintTask(0,500));
        pool.awaitTermination(2, TimeUnit.SECONDS);
        pool.shutdown();
    }

}

8核计算机的执行效果

原文地址:https://www.cnblogs.com/yumiaoxia/p/9058540.html

时间: 2024-08-01 22:06:13

多线程三(线程组和线程池)的相关文章

【Java基础】Java多线程之线程组和线程池

在上一篇文章中,讲述了线程的基本概念和用法,这里将继续讲述线程组和线程池的一些东西. 线程组:java.lang.ThreadGroup 1. 线程组的介绍 线程组表示一个线程的集合.此外,线程组也可以包含其他线程组.线程组构成一棵树,在树中,除了初始线程组外,每个线程组都有一个父线程组.允许线程访问有关自己的线程组的信息,但是不允许它访问有关其线程组的父线程组或其他任何线程组的信息.   2. 线程组的构造方法 ThreadGroup(String name) 构造一个新线程组. Thread

Java 多线程(七)——线程组与线程池

1 线程组 1.1 概述 Java中使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理.对线程组的控管理,即同时控制线程组里面的这一批线程. 用户创建的所有线程都属于指定线程组,如果没有显示指定属于哪个线程组,那么该线程就属于默认线程组(即main线程组).默认情况下,子线程和父线程处于同一个线程组. 只有在创建线程时才能指定其所在的线程组,线程运行中途不能改变它所属的线程组,也就是说线程一旦指定所在的线程组,就直到该线程结束. 线程组与线程之间结构类似于树形的结构: 1.2

黑马程序员——JAVA基础之Day24 多线程 ,死锁,线程间通信 ,线程组,线程池,定时器。

------- android培训.java培训.期待与您交流! ---------- Lock()实现提供了比使用synchronized方法和语句可获得更广泛的锁定操作. private Lock lock =new ReentrantLock(); 被锁的代码要用   lock.lock()                lock.unlock()    包括.其中用try   ...finally包围 同步:效率低,如果出现同步嵌套,会出现死锁.  但是安全. 死锁问题:两个或者两个以上

重踏学习Java路上_Day24(多线程锁,线程组,设计模式)

1:多线程(理解)     (1)JDK5以后的针对线程的锁定操作和释放操作        Lock锁    (2)死锁问题的描述和代码体现    (3)生产者和消费者多线程体现(线程间通信问题)         以学生作为资源来实现的                资源类:Student        设置数据类:SetThread(生产者)        获取数据类:GetThread(消费者)        测试类:StudentDemo                代码:      

JAVA学习笔记(四十一)-多线程与线程组

线程组ThreadGroup /* * 线程组ThreadGroup * * 结论: * 如果在设置线程组优先级之前设置线程优先级,则线程优先级不受线程组优先级限制 * 如果在设置线程组优先级之后设置线程优先级,则线程优先级不能超过线程组优先级 * * 线程的优先级,默认与启动它的父线程相同,但受到所有线程组的限制 */ public class Test02 { public static void main(String[] args) { System.out.println(Thread

Java多线程(十一):线程组

线程组 线程组可以批量管理线程和线程组对象. 一级关联 例子如下,建立一级关联. public class MyThread43 implements Runnable{ public void run() { try { while (!Thread.currentThread().isInterrupted()) { System.out.println("ThreadName = " + Thread.currentThread().getName()); Thread.slee

多线程 线程组

1 package org.zln.thread; 2 3 import java.util.Date; 4 5 /** 6 * Created by sherry on 000024/6/24 22:30. 7 */ 8 public class TestThreadGroup { 9 public static void main(String[] args) throws InterruptedException { 10 ThreadGroup group1 = new ThreadGr

java 多线程 24 : 线程组

线程组 可以把线程归属到某一个线程组中,线程组中可以有线程对象,也可以有线程组,组中还可以有线程,这样的组织结构有点类似于树的形式,如图所示: 线程组的作用是:可以批量管理线程或线程组对象,有效地对线程或线程组对象进行组织. 线程关联线程组:1级关联 所谓1级关联就是父对象中有子对象,但并不创建孙对象.这种情况在开发中很常见,比如创建一些线程时,为了有效对这些线程进行阻止管理,通常情况下是创建一个线程组,然后再将部分线程归属到该组中,以此来对零散的线程对象进行有效的管理. 看一下简单的1级关联的

Java多线程编程7--拾遗增补--线程组

可以把线程归属到某一个线程组中,线程组中可以有线程对象,也可以有线程组,组中还可以有线程.这样的组织结构有些类似于树的形式,如图所示. 线程组的作用是,可以批量的管理线程或线程组对象,有效地对线程或线程组对象进行组织. 1.线程对象关联线程组:1级关联 所谓的1级关联就是父对象中有子对象,但并不创建子孙对象.这种情况经常出现在开发中,比如创建一些线程时,为了有效地对这些线程进行组织管理,通常的情况下是创建一个线程组,然后再将部分线程归属到该组中.这样的处理可以对零散的线程对象进行有效的组织与规划