多线程——线程组

ThreadGroup 线程组:就是一组线程或者线程组,可以包含线程,也可以包含线程组,相信这个很好理解。

如下图:我可将这种关系,理解为父线程组,子线程组,线程。就像一棵树,本文会将末端的线程组成为“叶子节点

 

1、线程对象关联:

    1.1、1级关联

就是线程组中全部是线程,没有线程组,这样往下就没有可延伸的了,所以叫1级关联。

    2.2、多级关联

就是线程组中,还有线程组,可以向下延伸,就叫多级关联。

2、线程组自动归属

当创建了一个线程组,如果不显示地指定这个线程组是属于哪个线程组,那么这个线程组会自动地属于当前线程所属的线程组。

线程a属于线程组AA,如果线程a创建了一个线程组bb,又没有为bb指定属于哪个线程组,那么默认线程组bb会属于AA。

3、解析ThreadGroup类源码,看一下线程组的结构、可使用的方法。

private final ThreadGroup parent; 父线程组
    String name;  线程组名
    int maxPriority; 优先级
    boolean destroyed; 是否已被销毁
    boolean daemon;  是否是守护线程线程组
    boolean vmAllowSuspension; 是否允许虚拟机将此线程组中的线程全部挂起

    int nUnstartedThreads = 0; 线程组中未启动的线程数
    int nthreads; 组中线程数
    Thread threads[]; 线程数组

    int ngroups; 组中线程组的数目
ThreadGroup groups[]; 线程组 数组

    无参构造方法,如果创建一个线程组的时候,没有父线程组的话,那么它就是系统级别的线程组
    private ThreadGroup() {     // 构造函数
        this.name = "system";
        this.maxPriority = Thread.MAX_PRIORITY;
        this.parent = null;
    }
    创建线程组,名字为name,父组为当前线程所在的线程组
    public ThreadGroup(String name) {
        this(Thread.currentThread().getThreadGroup(), name);
    }
    指定父线程组,线程组名
    public ThreadGroup(ThreadGroup parent, String name) {
        this(checkParentAccess(parent), parent, name);
    }
    这个构造函数不能被显示调用来创建线程组,很多属性都是继承父组的,父组添加上这个子线程组
    Void unused参数在文中解释
    private ThreadGroup(Void unused, ThreadGroup parent, String name) {
        this.name = name;
        this.maxPriority = parent.maxPriority;
        this.daemon = parent.daemon;
        this.vmAllowSuspension = parent.vmAllowSuspension;
        this.parent = parent;
        parent.add(this);
    }
    检查这个父线程组是否可以接受此线程组,能就顺利返回null,不能就跑出异常
    private static Void checkParentAccess(ThreadGroup parent) {
        parent.checkAccess();
        return null;
    }

    public final String getName() {
        return name;
    }
    public final int getMaxPriority() {return maxPriority; }
public final boolean isDaemon() {return daemon; }
public synchronized boolean isDestroyed() {return destroyed;}
获取此线程组的父组,当然要经过检查,检查父组是否可以被引用(被开发人员使用)
public final ThreadGroup getParent() {
        if (parent != null)
            parent.checkAccess();
        return parent;
}
首先检查是否有这个权限去设置自己的“Daemon”。再去设置
public final void setDaemon(boolean daemon) {
        checkAccess();
        this.daemon = daemon;
}
设置优先级,
public final void setMaxPriority(int pri) {
        int ngroupsSnapshot;
        ThreadGroup[] groupsSnapshot;
        synchronized (this) { 锁住当前要改变优先级的线程组,等优先级设置成功后,才允许别的线程访问这个线程组
            checkAccess(); 检查权限
            if (pri < Thread.MIN_PRIORITY || pri > Thread.MAX_PRIORITY) {
                return; 优先级不能超出1~10
            }
如果自己就是系统级别线程组,那么就直接设置为pri,如果不是,那么就设置为pri和父组优先级中最小的,意思就是子优先级不能大于父组优先级
            maxPriority = (parent != null) ? Math.min(pri, parent.maxPriority) : pri;
            复制子线程组(其实是复制引用)
ngroupsSnapshot = ngroups;
            if (groups != null) {
                groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
            } else {
                groupsSnapshot = null;
            }
        }
        将子线程组的优先级改了
        for (int i = 0 ; i < ngroupsSnapshot ; i++) {
            groupsSnapshot[i].setMaxPriority(pri);
        }
}
检查权限,如果有权限,才会顺利地执行下去,如果没有,那么跑出异常
public final void checkAccess() {
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkAccess(this);
        }
}
判断此线程组是g吗?或者是g的某一个祖先线程组,如果是,返回true,否则false
public final boolean parentOf(ThreadGroup g) {
        for (; g != null ; g = g.parent) {
            if (g == this) {
                return true;
            }
        }
        return false;
}
返回活着的线程数(包括所有子线程组中的,直到叶子节点)
public int activeCount() {
        int result;
        int ngroupsSnapshot;
        ThreadGroup[] groupsSnapshot;
        synchronized (this) {
            if (destroyed) { 如果此线程组已销毁,那么就是0
                return 0;
            }
接下来就是复制子线程组 数组(复制引用)
            result = nthreads;
            ngroupsSnapshot = ngroups;
            if (groups != null) {
                groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
            } else {
                groupsSnapshot = null;
            }
        }
以递归的方式 统计所有子线程中的活着的线程总数
        for (int i = 0 ; i < ngroupsSnapshot ; i++) {
            result += groupsSnapshot[i].activeCount();
        }
        return result;
}
从此线程组中获取线程,存在list[]数组中,操作数组即可操作这些线程,其实是将线程的引用存在list[]中,返回实际获取的线程数。
public int enumerate(Thread list[]) {
        checkAccess();
        return enumerate(list, 0, true);
}
同上,recurse参数,true代表如果此线程组中线程不够的话,就去子线程组中取线程,一定要装满list[]。
public int enumerate(Thread list[], boolean recurse) {
        checkAccess();
        return enumerate(list, 0, recurse);
}
上面两个方法的具体实现,list数组是有长度的,意思是从此线程组中获取线程,从list的第n个位置开始放,如果recurse是true,那就要list装满,此线程组中的线程不够的话,就去子线程组中去线程,如果recurse是false,那就不用去子线程组了,就在此线程组中,能有多少取多少,能装满就装满,装不满就算了。
private int enumerate(Thread list[], int n, boolean recurse) {
        int ngroupsSnapshot = 0;
        ThreadGroup[] groupsSnapshot = null;
        synchronized (this) {
            if (destroyed) {
                return 0;
            }
            int nt = nthreads;
            if (nt > list.length - n) {
                nt = list.length - n;
            }
            for (int i = 0; i < nt; i++) {
                if (threads[i].isAlive()) {
                    list[n++] = threads[i];
                }
            }
            if (recurse) {
                ngroupsSnapshot = ngroups;
                if (groups != null) {
                    groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
                } else {
                    groupsSnapshot = null;
                }
            }
        }
        if (recurse) {
            for (int i = 0 ; i < ngroupsSnapshot ; i++) {
                n = groupsSnapshot[i].enumerate(list, n, true);
            }
        }
        return n;
}
返回活着的子线程组,递归方式向下计数,直到叶子节点为止
public int activeGroupCount() {
        int ngroupsSnapshot;
        ThreadGroup[] groupsSnapshot;
        synchronized (this) {
            if (destroyed) {
                return 0;
            }
            ngroupsSnapshot = ngroups;
            if (groups != null) {
                groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
            } else {
                groupsSnapshot = null;
            }
        }
        int n = ngroupsSnapshot;
        for (int i = 0 ; i < ngroupsSnapshot ; i++) {
            n += groupsSnapshot[i].activeGroupCount();
        }
        return n;
}
从此线程组中获取线程组,存在list[]数组中,操作数组即可操作这些线程组,其实是将线程组的引用存在list[]中,返回实际获取的线程组数。
public int enumerate(ThreadGroup list[]) {
        checkAccess();
        return enumerate(list, 0, true);
}
public int enumerate(ThreadGroup list[], boolean recurse) {
        checkAccess();
        return enumerate(list, 0, recurse);
}
同获取线程的意思是一样的。
private int enumerate(ThreadGroup list[], int n, boolean recurse) {
        int ngroupsSnapshot = 0;
        ThreadGroup[] groupsSnapshot = null;
        synchronized (this) {
            if (destroyed) {
                return 0;
            }
            int ng = ngroups;
            if (ng > list.length - n) {
                ng = list.length - n;
            }
            if (ng > 0) {
                System.arraycopy(groups, 0, list, n, ng);
                n += ng;
            }
            if (recurse) {
                ngroupsSnapshot = ngroups;
                if (groups != null) {
                    groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
                } else {
                    groupsSnapshot = null;
                }
            }
        }
        if (recurse) {
            for (int i = 0 ; i < ngroupsSnapshot ; i++) {
                n = groupsSnapshot[i].enumerate(list, n, true);
            }
        }
        return n;
}

停止此线程组中的所有线程,@Deprecated注解:不安全的,不推荐使用,会报警告
@Deprecated
public final void stop() {
        if (stopOrSuspend(false)) //先停止除当前运行的线程外的所有线程,想想是不是这个道理,当前线程一定要最后来停止
            Thread.currentThread().stop(); 然后停止当前线程
}

停止或者挂起此线程组中的所有线程(包括所以子组,直到叶子节点),如果当前运行的线程是属于此线程组的线程,或者某个子孙线程组的线程,那么不会停止或者挂起它,除此之外的所有线程,如果suspend参数为true,就全部挂起,为false就全部停止。
private boolean stopOrSuspend(boolean suspend) {
        boolean suicide = false;
        Thread us = Thread.currentThread();
        int ngroupsSnapshot;
        ThreadGroup[] groupsSnapshot = null;
        synchronized (this) {
            checkAccess();
            for (int i = 0 ; i < nthreads ; i++) {
                当前运行的线程属于此线程组,那么不会停止或者挂起它
if (threads[i]==us) 
                    suicide = true;
                else if (suspend) 根据参数决定是否将线程挂起
                    threads[i].suspend();
                else 否则就停止
                    threads[i].stop();
            }

            ngroupsSnapshot = ngroups;
            if (groups != null) {
                groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
            }
        }
递归子组,直到叶子节点,都要执行stopOrSupend
        for (int i = 0 ; i < ngroupsSnapshot ; i++)
            suicide = groupsSnapshot[i].stopOrSuspend(suspend) || suicide;

        return suicide;
}

唤醒此线程组中的所有线程(包括所有子线程组,直到叶子节点)
@Deprecated
    @SuppressWarnings("deprecation")
    public final void resume() {
        int ngroupsSnapshot;
        ThreadGroup[] groupsSnapshot;
        synchronized (this) {
            checkAccess();
            for (int i = 0 ; i < nthreads ; i++) {
                threads[i].resume();
            }
            ngroupsSnapshot = ngroups;
            if (groups != null) {
                groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
            } else {
                groupsSnapshot = null;
            }
        }
        for (int i = 0 ; i < ngroupsSnapshot ; i++) {
            groupsSnapshot[i].resume();
        }
    }
销毁此线程组及其所有子组(直到叶子节点),但是前提是所有线程组中的线程都已经被销毁了,想想如果有线程还活着,此销毁会杀掉它们的。
public final void destroy() {
        int ngroupsSnapshot;
        ThreadGroup[] groupsSnapshot;
        synchronized (this) {
            checkAccess();
            if (destroyed || (nthreads > 0)) {
                throw new IllegalThreadStateException();
            }
            ngroupsSnapshot = ngroups;
            if (groups != null) {
                groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
            } else {
                groupsSnapshot = null;
            }
            if (parent != null) {
                destroyed = true;
                ngroups = 0;
                groups = null;
                nthreads = 0;
                threads = null;
            }
        }
        for (int i = 0 ; i < ngroupsSnapshot ; i += 1) {
            groupsSnapshot[i].destroy();
        }
        if (parent != null) {
            parent.remove(this);
        }
}

添加线程组g到此线程组中。如果此线程组是叶子节点,那就扩展4个子线程组空间出来如果子线程组数组满了,那就扩大两倍的空间
private final void add(ThreadGroup g){
        synchronized (this) {
            if (destroyed) {
                throw new IllegalThreadStateException();
            }
            if (groups == null) { 如果此线程组是叶子节点,那就扩展4个子线程组空间出来
                groups = new ThreadGroup[4];
            } else if (ngroups == groups.length) { 如果子线程组数组满了,那就扩大两倍的空间
                groups = Arrays.copyOf(groups, ngroups * 2);
            }
            groups[ngroups] = g;
            ngroups++;
        }
}
删除此线程组中的g子组
private void remove(ThreadGroup g) {
        synchronized (this) {
            if (destroyed) {
                return;
            }
            for (int i = 0 ; i < ngroups ; i++) {
                if (groups[i] == g) {
                    ngroups -= 1;
                    System.arraycopy(groups, i + 1, groups, i, ngroups - i);
                    groups[ngroups] = null;
                    break;
                }
            }
            if (nthreads == 0) { 如果此线程组中没有线程了,
                notifyAll(); 唤醒等待此锁的所有线程
            }
            if (daemon && (nthreads == 0) &&
                (nUnstartedThreads == 0) && (ngroups == 0))
            { 如果此线程组是守护线程的线程组,并且已经空了,那就执行一次销毁
                destroy(); 
            }
        }
}

添加一个未启动的线程到此线程组中,其实只是简单的nUnstartedThreads++。
void addUnstarted() {
        synchronized(this) {
            if (destroyed) {
                throw new IllegalThreadStateException();
            }
            nUnstartedThreads++;
        }
}
添加一个线程到此线程组中
void add(Thread t) {
        synchronized (this) {
            if (destroyed) {
                throw new IllegalThreadStateException();
            }
            if (threads == null) { 如果线程数组为空,则扩展4个线程空间
                threads = new Thread[4];
            } else if (nthreads == threads.length) { 如果数组满了,就扩展为2倍空间
                threads = Arrays.copyOf(threads, nthreads * 2);
            }
            threads[nthreads] = t;
            nthreads++;
            nUnstartedThreads--; 特别注意此变量的变化,一定要准确,正确,否则会出问题。
        }
    }
线程启动失败,就可以用这个方法,先将线程t从线程数组中删除,然后nUnstartedThreads++;
void threadStartFailed(Thread t) {
        synchronized(this) {
            remove(t);
            nUnstartedThreads++;
        }
}
线程结束了,那么就可以用此方法,从线程组中删除此线程。
void threadTerminated(Thread t) {
        synchronized (this) {
            remove(t);

            if (nthreads == 0) {
                notifyAll();
            }
            if (daemon && (nthreads == 0) &&
                (nUnstartedThreads == 0) && (ngroups == 0))
            {
                destroy();
            }
        }
}
从线程组中删除这个线程t
private void remove(Thread t) {
        synchronized (this) {
            if (destroyed) {
                return;
            }
            for (int i = 0 ; i < nthreads ; i++) {
                if (threads[i] == t) {
                    System.arraycopy(threads, i + 1, threads, i, --nthreads - i);
                    threads[nthreads] = null;
                    break;
                }
            }
        }
    }
输出此线程组的情况,仅用于测试用
public void list() {
        list(System.out, 0);
}
    void list(PrintStream out, int indent) {
        int ngroupsSnapshot;
        ThreadGroup[] groupsSnapshot;
        synchronized (this) {
            for (int j = 0 ; j < indent ; j++) {
                out.print(" ");
            }
            out.println(this);
            indent += 4;
            for (int i = 0 ; i < nthreads ; i++) {
                for (int j = 0 ; j < indent ; j++) {
                    out.print(" ");
                }
                out.println(threads[i]);
            }
            ngroupsSnapshot = ngroups;
            if (groups != null) {
                groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
            } else {
                groupsSnapshot = null;
            }
        }
        for (int i = 0 ; i < ngroupsSnapshot ; i++) {
            groupsSnapshot[i].list(out, indent);
        }
    }
设置是否允许虚拟机拥有挂起此线程组中线程的权力,b为true,则允许,false不允许
@Deprecated
    public boolean allowThreadSuspension(boolean b) {
        this.vmAllowSuspension = b;
        if (!b) {
            VM.unsuspendSomeThreads();
        }
        return true;
    }

说一下,线程的优先级共10级,1~10,1是最低,10是最高。所以系统级别的线程组默认优先级是10,最高了,因为它没有父线程组。如果创建线程组的时候,如果没有指定优先级,那么就继承父组的优先级。

checkParentAccess(parent)这个方法:其实是最终让Java的SecurityManager去检查,检查内容:parent是否为null;parent是否为系统级别的线程组,再查看是否这个系统线程组可以添加子线程组。

Void unused参数:注意此处的Void是首字母大写,不是void,Void是个类,继承Object,没什么用,这么写只是为了能够让做一次checkParentAccess(parent)检查。

@SuppressWarnings("")注解:告诉编译器忽略指定的警告,在编译完成之后不要显示该警告。

@Deprecated注解:不安全的,不推荐使用,会报警告。

能够用循环处理,尽量不要递归,因为深度很大的递归会消耗很大的内存。

Arrays.copyOf()和System.arraycopy()区别可以查看文章,博客中有。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在JMeter中,压测多个线程组的方法取决于您希望这些线程组是并行运行还是串行运行。 如果您希望这些线程组并行运行,您可以按照以下步骤进行设置: 1. 在测试计划中创建多个线程组,每个线程组代表一个特定的场景或压力测试需求。 2. 在每个线程组中配置相应的线程数和循环次数等参数,以模拟不同的用户并发访问。 3. 确保这些线程组没有包含互斥控制器,以允许它们并行执行。 4. 运行测试计划,JMeter将同时执行这些线程组,并生成相应的压力。 如果您希望这些线程组串行运行,您可以按照以下步骤进行设置: 1. 在测试计划中创建多个线程组,每个线程组代表一个特定的场景或压力测试需求。 2. 在每个线程组中配置相应的线程数和循环次数等参数,以模拟不同的用户并发访问。 3. 使用逻辑控制器(如简单控制器或事务控制器)将这些线程组连接起来,按照您希望的顺序设置它们的执行顺序。 4. 运行测试计划,JMeter将按照您设置的顺序依次执行这些线程组,逐步模拟不同的用户访问。 通过以上方法,您可以在JMeter中实现多个线程组的压测,并根据需要选择并行或串行运行。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [JMeter多个线程组的使用说明](https://blog.csdn.net/weixin_38089131/article/details/114872906)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [JMeter压力测试-多线程组并发测试(多场景混合并发)](https://blog.csdn.net/weixin_42180610/article/details/124257057)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值