计算并发用户数的五种方法
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 大核心参数
七大参数
- corePoolSize 核心线程数目 - 池中会保留的最多线程数
- maximumPoolSize 最大线程数目 - 核心线程+救急线程的最大数目
- keepAliveTime 生存时间 - 救急线程的生存时间,生存时间内没有新任务,此线程资源会释放
- unit 时间单位 - 救急线程的生存时间单位,如秒、毫秒等
- workQueue - 当没有空闲核心线程时,新来任务会加入到此队列排队,队列满会创建救急线程执行任务
- threadFactory 线程工厂 - 可以定制线程对象的创建,例如设置线程名字、是否是守护线程等
- handler 拒绝策略 - 当所有线程都在繁忙,workQueue 也放满时,会触发拒绝策略
-
workQueue满了,救急线程数目也达到最大了,后面来任务直接抛异常
java.util.concurrent.ThreadPoolExecutor.AbortPolicy
-
workQueue满了,救急线程数目也达到最大了,新加的任务由调用者执行(谁去submit任务谁就去执行任务)
java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy
-
workQueue满了,救急线程数目也达到最大了,后面来的任务之间丢弃。
java.util.concurrent.ThreadPoolExecutor.DiscardPolicy
-
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线程
运行结果:
以上是关于计算并发用户数的五种方法的主要内容,如果未能解决你的问题,请参考以下文章
Java 并发 -- 线程状态(Java线程六种状态操作系统层次的五种状态)线程池(七大核心参数)wait vs sleep
Java 并发 -- 线程状态(Java线程六种状态操作系统层次的五种状态)线程池(七大核心参数)wait vs sleep