zoukankan      html  css  js  c++  java
  • JAVA 模拟瞬间高并发

          前些日子接到了一个面试电话。面试内容我印象非常深,怎样模拟一个并发?当时我的回答尽管也能够算是正确的,但自己感觉缺乏实际能够操作的细节,仅仅有一个大概的描写叙述。

          当时我的回答是:“线程所有在同一节点wait,然后在某个节点notifyAll。”

          面试官:“那你听说过惊群效应吗?”

          我:“我没有听过这个名词,但我知道瞬间唤醒全部的线程,会让CPU负载瞬间加大。

          面试官:“那你有什么改进的方式吗?”

          我:“採用堵塞技术。在某个节点将全部的线程堵塞,在利用条件。线程的个数达到一定数量的时候。打开堵塞。

          面试官好像是比較惬意,结束了这个话题。

          面试结束后,我回头这个块进行了思考。要怎样进行堵塞呢?我首先有一个思路就是。利用AtoInteger计算线程数,再利用synchronize方法块堵塞一个线程,依据AtoInteger的推断,运行sleep。

          代码例如以下:

    /**
     * Created with IntelliJ IDEA.
     * User: 菜鸟大明
     * Date: 14-10-21
     * Time: 下午4:34
     * To change this template use File | Settings | File Templates.
     */
    public class CountDownLatchTest1 implements Runnable{
        final AtomicInteger number = new AtomicInteger();
        volatile boolean bol = false;
    
        @Override
        public void run() {
            System.out.println(number.getAndIncrement());
            synchronized (this) {
                try {
                    if (!bol) {
                        System.out.println(bol);
                        bol = true;
                        Thread.sleep(10000);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("并发数量为" + number.intValue());
            }
    
        }
    
        public static void main(String[] args) {
            ExecutorService pool = Executors. newCachedThreadPool();
            CountDownLatchTest1 test = new CountDownLatchTest1();
            for (int i=0;i<10;i++) {
                pool.execute(test);
            }
        }
    }
    
    结果为:

    0
    2
    1
    4
    3
    false
    5
    6
    7
    8
    9
    并发数量为10
    并发数量为10
    并发数量为10
    并发数量为10
    并发数量为10
    并发数量为10
    并发数量为10
    并发数量为10
    并发数量为10
    并发数量为10

    从结果上来看,应该是能够解决这个问题,利用了同步锁,volatile攻克了同一时候释放的问题,难点就在于开关。

    后来查找资料,找到了一个CountDownLatch的类。专门干这个的

    CountDownLatch是一个同步辅助类,宛如倒计时计数器,创建对象时通过构造方法设置初始值,调用CountDownLatch对象的await()方法则处于等待状态。调用countDown()方法就将计数器减1,当计数到达0时,则全部等待者或单个等待者開始运行。


    构造方法參数指定了计数的次数

    new CountDownLatch(1)

    countDown方法。当前线程调用此方法,则计数减一

    cdAnswer.countDown();

    awaint方法,调用此方法会一直堵塞当前线程,直到计时器的值为0

    cdOrder.await();

    直接贴代码,转载的代码

    /**
     *
     * @author Administrator
     *该程序用来模拟发送命令与运行命令,主线程代表指挥官。新建3个线程代表战士,战士一直等待着指挥官下达命令,
     *若指挥官没有下达命令,则战士们都必须等待。

    一旦命令下达,战士们都去运行自己的任务。指挥官处于等待状态,战士们任务运行完成则报告给  *指挥官。指挥官则结束等待。  */ public class CountdownLatchTest {     public static void main(String[] args) {         ExecutorService service = Executors.newCachedThreadPool(); //创建一个线程池         final CountDownLatch cdOrder = new CountDownLatch(1);//指挥官的命令。设置为1,指挥官一下达命令。则cutDown,变为0,战士们运行任务         final CountDownLatch cdAnswer = new CountDownLatch(3);//由于有三个战士,所以初始值为3,每个战士运行任务完成则cutDown一次,当三个都运行完成,变为0。则指挥官停止等待。         for(int i=0;i<3;i++){             Runnable runnable = new Runnable(){                 public void run(){                     try {                         System.out.println("线程" + Thread.currentThread().getName() +                                 "正准备接受命令");                         cdOrder.await(); //战士们都处于等待命令状态                         System.out.println("线程" + Thread.currentThread().getName() +                                 "已接受命令");                         Thread.sleep((long)(Math.random()*10000));                         System.out.println("线程" + Thread.currentThread().getName() +                                 "回应命令处理结果");                     } catch (Exception e) {                         e.printStackTrace();                     } finally {                         cdAnswer.countDown(); //任务运行完成,返回给指挥官,cdAnswer减1。                     }                 }             };             service.execute(runnable);//为线程池加入任务         }         try {             Thread.sleep((long)(Math.random()*10000));             System.out.println("线程" + Thread.currentThread().getName() +                     "即将公布命令");             cdOrder.countDown(); //发送命令,cdOrder减1,处于等待的战士们停止等待转去运行任务。             System.out.println("线程" + Thread.currentThread().getName() +                     "已发送命令,正在等待结果");             cdAnswer.await(); //命令发送后指挥官处于等待状态。一旦cdAnswer为0时停止等待继续往下运行             System.out.println("线程" + Thread.currentThread().getName() +                     "已收到全部响应结果");         } catch (Exception e) {             e.printStackTrace();         } finally {         }         service.shutdown(); //任务结束。停止线程池的全部线程     } }

    运行结果:

    线程pool-1-thread-2正准备接受命令
    线程pool-1-thread-3正准备接受命令
    线程pool-1-thread-1正准备接受命令
    线程main即将公布命令
    线程pool-1-thread-2已接受命令
    线程pool-1-thread-3已接受命令
    线程pool-1-thread-1已接受命令
    线程main已发送命令,正在等待结果
    线程pool-1-thread-2回应命令处理结果
    线程pool-1-thread-1回应命令处理结果
    线程pool-1-thread-3回应命令处理结果
    线程main已收到全部响应结果

    上述也是一种实现方式,用countDownLatch的await()方法,取代了synchronize 和 sleep的堵塞功能,通过countDown的方法来当做开关,和计算线程数量的一种方式。

    差别的话,肯定是后者会好一些,由于第一种方式依靠sleep(xxx)来堵塞把握不好最短时间,太短了,可能来没有达到固定线程数就会打开开关。

    至于两者性能上的差别,眼下我还不得而知,有机会測试一下。



  • 相关阅读:
    jqueryeasyui中创建复杂布局
    内表查询用到外表
    某人对为什么不搞.net,不搞web的原因
    眼睛痛的一些原因
    控件包含代码块(即 <% ... %>),因此无法修改控件集合(用户自定义控件中)
    明天留着看
    SqlServer 数据归档
    从 char 数据类型到 datetime 数据类型的转换导致 datetime 值越界。
    生成sql 脚本没有索引
    sqlserver 时间间隔
  • 原文地址:https://www.cnblogs.com/cxchanpin/p/6985012.html
Copyright © 2011-2022 走看看