计算并发用户数的五种方法

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了计算并发用户数的五种方法相关的知识,希望对你有一定的参考价值。

参考技术A    一般来说,利用以下经验公式进行估算系统的平均并发用户数和峰值数据

  1)平均并发用户数为 C = nL/T

  2)并发用户数峰值 C‘ = C + 3*根号C

    C是平均并发用户数,n是login session的数量,L是login session的平均长度,T是值考察的时间长度

    C’是并发用户数峰值

假设系统A,该系统有3000个用户,平均每天大概有400个用户要访问该系统(可以从系统日志从获得),对于一个典型用户来说,一天之内用户从登陆到退出的平均时间为4小时,而在一天之内,用户只有在8小时之内会使用该系统。

  那么,

  平均并发用户数为:C = 400*4/8 = 200

  并发用户数峰值为:C‘ = 200 + 3*根号200 = 243

某公司为其170000名员工设计了一个薪酬系统,员工可进入该系统查询自己的薪酬信息,但并不是每个人都会用这个系统,假设只有50%的人会定期用该系统,这些人里面有70%是在每个月的最后一周使用一次该系统,且平均使用系统时间为5分钟。

  则一个月最后一周的平均并发用户数为(朝九晚五):

  n = 170000*0.5*0.7/5 = 11900

  C= 11900*5/60/8 = 124

  吞吐量计算为:F = Vu * R / T 单位为个/s

    F为事务吞吐量,Vu为虚拟用户数个数,R为每个虚拟用户发出的请求数,T为处理这些请求所花费的时间

  对绝大多数场景,我们用(用户总量/统计时间)*影响因子(一般为3)来进行估算并发量。

  比如,以乘坐地铁为例子,每天乘坐人数为5万人次,每天早高峰是7到9点,晚高峰是6到7点,根据8/2原则,80%的乘客会在高峰期间乘坐地铁,则每秒到达地铁检票口的人数为50000*80%/(3*60*60)=3.7,约4人/S,考虑到安检,入口关闭等因素,实际堆积在检票口的人数肯定比这个要大,假定每个人需要3秒才能进站,那实际并发应为4人/s*3s=12,当然影响因子可以根据实际情况增大!

  比如一个网站,每天的PV大概1000w,根据2/8原则,我们可以认为这1000w pv的80%是在一天的9个小时内完成的(人的精力有限),那么TPS为:

  1000w*80%/(9*3600)=246.92个/s,取经验因子3,则并发量应为:

  246.92*3=740

   公式为 C = (Think time + 1)*TPS

五、根据系统用户数计算:

   并发用户数 = 系统最大在线用户数的8%到12%

备注:本人目前在网上只找到了这5种,计算并发用户数的方法,其他计算方法,欢迎大家留言补充

---------------------

原文:https://blog.csdn.net/qq_23101033/article/details/74977874

Java 并发 -- 线程状态(Java线程六种状态操作系统层次的五种状态)线程池(七大核心参数)wait vs sleep

文章目录


1. 线程状态

要求

  • 掌握 Java 线程六种状态
  • 掌握 Java 线程状态转换
  • 能理解五种状态与六种状态两种说法的区别

1.1 Java线程六种状态及转换


分别是

  • 新建
    • 当一个线程对象被创建,但还未调用 start 方法时处于新建状态
    • 此时未与操作系统底层线程关联
  • 可运行
    • 调用了 start 方法,就会由新建进入可运行
    • 此时与底层线程关联,由操作系统调度执行
  • 终结
    • 线程内代码已经执行完毕,由可运行进入终结
    • 此时会取消与底层线程关联
  • 阻塞
    • 当获取锁失败后,由可运行进入 Monitor 的阻塞队列阻塞,此时不占用 cpu 时间
    • 当持锁线程释放锁时,会按照一定规则唤醒阻塞队列中的阻塞线程,唤醒后的线程进入可运行状态
  • 等待
    • 当获取锁成功后,但由于条件不满足,调用了 wait() 方法,此时从可运行状态释放锁进入 Monitor 等待集合等待,同样不占用 cpu 时间
    • 当其它持锁线程调用 notify() 或 notifyAll() 方法,会按照一定规则唤醒等待集合中的等待线程,恢复为可运行状态
  • 有时限等待
    • 当获取锁成功后,但由于条件不满足,调用了 wait(long) 方法,此时从可运行状态释放锁进入 Monitor 等待集合进行有时限等待,同样不占用 cpu 时间
    • 当其它持锁线程调用 notify() 或 notifyAll() 方法,会按照一定规则唤醒等待集合中的有时限等待线程,恢复为可运行状态,并重新去竞争锁
    • 如果等待超时,也会从有时限等待状态恢复为可运行状态,并重新去竞争锁
    • 还有一种情况是调用 sleep(long) 方法也会从可运行状态进入有时限等待状态,但与 Monitor 无关,不需要主动唤醒,超时时间到自然恢复为可运行状态

其它情况(只需了解)

  • 可以用 interrupt() 方法打断等待有时限等待的线程,让它们恢复为可运行状态
  • park,unpark 等方法也可以让线程等待和唤醒

1.2 操作系统层次的五种状态

这是从 操作系统 层面来描述的

  • 【初始状态】仅是在语言层面创建了线程对象,还未与操作系统线程关联

  • 【可运行状态】(就绪状态)指该线程已经被创建(与操作系统线程关联),可以由 CPU 调度执行

  • 【运行状态】指获取了 CPU 时间片运行中的状态

    • 当 CPU 时间片用完,会从【运行状态】转换至【可运行状态】,会导致线程的上下文切换
  • 【阻塞状态】

    • 如果调用了阻塞 API,如 BIO 读写文件,这时该线程实际不会用到 CPU,会导致线程上下文切换,进入【阻塞状态】
    • 等 BIO 操作完毕,会由操作系统唤醒阻塞的线程,转换至【可运行状态】
    • 与【可运行状态】的区别是,对【阻塞状态】的线程来说只要它们一直不唤醒,调度器就一直不会考虑调度它们
  • 【终止状态】表示线程已经执行完毕,生命周期已经结束,不会再转换为其它状态


2. 线程池(七大核心参数)

要求

  • 掌握线程池的 7 大核心参数

七大参数

  1. corePoolSize 核心线程数目 - 池中会保留的最多线程数
  2. maximumPoolSize 最大线程数目 - 核心线程+救急线程的最大数目
  3. keepAliveTime 生存时间 - 救急线程的生存时间,生存时间内没有新任务,此线程资源会释放
  4. unit 时间单位 - 救急线程的生存时间单位,如秒、毫秒等
  5. workQueue - 当没有空闲核心线程时,新来任务会加入到此队列排队,队列满会创建救急线程执行任务
  6. threadFactory 线程工厂 - 可以定制线程对象的创建,例如设置线程名字、是否是守护线程等
  7. handler 拒绝策略 - 当所有线程都在繁忙,workQueue 也放满时,会触发拒绝策略
    1. workQueue满了,救急线程数目也达到最大了,后面来任务直接抛异常 java.util.concurrent.ThreadPoolExecutor.AbortPolicy

    2. workQueue满了,救急线程数目也达到最大了,新加的任务由调用者执行(谁去submit任务谁就去执行任务) java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy

    3. workQueue满了,救急线程数目也达到最大了,后面来的任务之间丢弃。java.util.concurrent.ThreadPoolExecutor.DiscardPolicy

    4. workQueue满了,救急线程数目也达到最大了,直接丢弃最早排队任务 (丢弃排队时间最长的任务,把新的任务添加到workQueue)java.util.concurrent.ThreadPoolExecutor.DiscardOldestPolicy

示例代码:

import org.slf4j.Logger;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static day02.LoggerUtils.*;

public class TestThreadPoolExecutor 

    public static void main(String[] args) throws InterruptedException 
        AtomicInteger c = new AtomicInteger(1);
        ArrayBlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(2);
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
                2,
                3,
                0,
                TimeUnit.MILLISECONDS,
                queue,
                r -> new Thread(r, "myThread" + c.getAndIncrement()),
                new ThreadPoolExecutor.DiscardOldestPolicy());
        showState(queue, threadPool);
        threadPool.submit(new MyTask("1", 3600000));
        showState(queue, threadPool);
        threadPool.submit(new MyTask("2", 3600000));
        showState(queue, threadPool);
        threadPool.submit(new MyTask("3"));
        showState(queue, threadPool);
        threadPool.submit(new MyTask("4"));
        showState(queue, threadPool);
        threadPool.submit(new MyTask("5", 3600000));
        showState(queue, threadPool);
        threadPool.submit(new MyTask("6"));
        showState(queue, threadPool);
    

    private static void showState(ArrayBlockingQueue<Runnable> queue, ThreadPoolExecutor threadPool) 
        try 
            Thread.sleep(300);
         catch (InterruptedException e) 
            e.printStackTrace();
        
        List<Object> tasks = new ArrayList<>();
        for (Runnable runnable : queue) 
            try 
                Field callable = FutureTask.class.getDeclaredField("callable");
                callable.setAccessible(true);
                Object adapter = callable.get(runnable);
                Class<?> clazz = Class.forName("java.util.concurrent.Executors$RunnableAdapter");
                Field task = clazz.getDeclaredField("task");
                task.setAccessible(true);
                Object o = task.get(adapter);
                tasks.add(o);
             catch (Exception e) 
                e.printStackTrace();
            
        
        main.debug("pool size: , queue: ", threadPool.getPoolSize(), tasks);
    

    static class MyTask implements Runnable 
        private final String name;
        private final long duration;

        public MyTask(String name) 
            this(name, 0);
        

        public MyTask(String name, long duration) 
            this.name = name;
            this.duration = duration;
        

        @Override
        public void run() 
            try 
                LoggerUtils.get("myThread").debug("running..." + this);
                Thread.sleep(duration);
             catch (InterruptedException e) 
                e.printStackTrace();
            
        

        @Override
        public String toString() 
            return "MyTask(" + name + ")";
        
    

运行结果:


3. wait vs sleep

要求

  • 能够说出二者区别

一个共同点,三个不同点

共同点

  • wait() ,wait(long) 和 sleep(long) 的效果都是让当前线程暂时放弃 CPU 的使用权,进入阻塞状态。

不同点

  • 方法归属不同

    • sleep(long) 是 Thread 的静态方法
    • 而 wait(),wait(long) 都是 Object 的成员方法,每个对象都有
  • 醒来时机不同

    • 执行 sleep(long) 和 wait(long) 的线程都会在等待相应毫秒后醒来
    • wait(long) 和 wait() 还可以被 notify 唤醒,wait() 如果不唤醒就一直等下去
    • 它们都可以被打断唤醒(调用线程的 interrupt() 方法)
  • 锁特性不同(重点)

    • wait 方法的调用必须先获取 wait 对象的锁,而 sleep 则无此限制
    • wait 方法执行后会释放对象锁,允许其它线程获得该对象锁(我放弃 cpu,但你们还可以用)
    • 而 sleep 如果在 synchronized 代码块中执行,并不会释放对象锁(我放弃 cpu,你们也用不了)

下面是一些示例代码。


3.1 wait() 必须配合锁一起使用

    static final Object LOCK = new Object();

    public static void main(String[] args) throws InterruptedException 
        LOCK.wait();
    

运行结果:

    static final Object LOCK = new Object();

    public static void main(String[] args) throws InterruptedException 
        synchronized (LOCK)
            LOCK.wait();
        
    


3.2 wait 方法执行后会释放锁 sleep则不会

示例代码:

运行结果:

示例代码:

运行结果:


3.3 interrupt()方法可以提前唤醒sleep线程和wait线程


运行结果:



以上是关于计算并发用户数的五种方法的主要内容,如果未能解决你的问题,请参考以下文章

编写测试用例常用的五种方法

php获取用户ip的五种方式

Java 并发 -- 线程状态(Java线程六种状态操作系统层次的五种状态)线程池(七大核心参数)wait vs sleep

Java 并发 -- 线程状态(Java线程六种状态操作系统层次的五种状态)线程池(七大核心参数)wait vs sleep

详解PHP实现定时任务的五种方法

详解PHP实现定时任务的五种方法