11 多线程累加问题

原创 2016年08月30日 20:06:26

前言

今天 无聊闲想的时候, 突然想到了一下多线程相关的问题
1. 累增的问题, 也就是对于一个int多个[x]线程对其进行++操作, 每个线程操作N次
    1.1 常规场景下面, 多线程进行处理, 得到的结果应该是小于x * N, 因为++并非原子操作
    1.2 在常规的场景下面, 使用synchronized[Lock相关api], 进行处理, 虽然能够保证正确性, 开销似乎是太大了一些[悲观锁]
    1.3 使用AtomicInteger就不一样了, CAS操作保证了线程的安全[乐观锁][忙等]

问题描述

对于一个整数, 多个线程进行多次累加操作, 确保正确性 !

思路

当然 这里确保正确性并非此帖核心问题, 因为 上面已经介绍了嘛, synchronized关键字, Lock相关api, Atomic相关api都可以实现

详细问题请往后看

参考代码

/**
 * file name : Test19IncInConcurrent.java
 * created at : 下午6:23:41 2016年8月30日
 * created by 970655147
 */

package com.hx.test01;

public class Test19IncInConcurrent {

    // 并发场景下面的累增测试
    public static void main(String[] args) throws Exception {

        log.logPatternChain = null;

        int[] res = {0, 0, 0, 0 };

        IdxGenerator idx = new IdxGenerator(0);
        res[idx.nextId()] = inc01();
        res[idx.nextId()] = inc02();
        res[idx.nextId()] = inc02Lock();
        res[idx.nextId()] = inc03();

        log(res);

    }

    // threadNums, synchronized on 'outer'
    static int THREAD_NUM = 10;
//  static boolean syncOuter = true;
    static boolean syncOuter = false;
    // inc per thread
    static int INC_NUM = 1000000;

    // case 1. not synchronized multi-threads
    public static int inc01() throws Exception {
        MyInteger res = new MyInteger(0);
        Runnable[] runnables = new Runnable[THREAD_NUM];
        for(int i=0; i<runnables.length; i++) {
            runnables[i] = new Case01(res);
        }
        doExec(runnables);

        return res.i;
    }

    // case 2. synchronized multi-threads
    public static int inc02() throws Exception {
        MyInteger res = new MyInteger(0);
        Runnable[] runnables = new Runnable[THREAD_NUM];
        for(int i=0; i<runnables.length; i++) {
            runnables[i] = new Case02(res);
        }
        doExec(runnables);

        return res.i;
    }

    // case 2Lock. synchronized multi-threads
    public static int inc02Lock() throws Exception {
        MyInteger res = new MyInteger(0);
        Runnable[] runnables = new Runnable[THREAD_NUM];
        for(int i=0; i<runnables.length; i++) {
            runnables[i] = new Case02Lock(res);
        }
        doExec(runnables);

        return res.i;
    }

    // case 3. AtomicInteger
    public static int inc03() throws Exception {
        AtomicInteger res = new AtomicInteger();
        Runnable[] runnables = new Runnable[THREAD_NUM];
        for(int i=0; i<runnables.length; i++) {
            runnables[i] = new Case03(res);
        }
        doExec(runnables);

        return res.get();
    }

    // doExec, run & join
    private static void doExec(Runnable[] runnables) throws Exception {
        long start = Tools.now();
        Thread[] threads = new Thread[runnables.length ];
        for(int i=0; i<runnables.length; i++) {
            threads[i] = new Thread(runnables[i]);
            threads[i].start();
        }
        for(int i=0; i<threads.length; i++) {
            threads[i].join();
        }

        long spent = Tools.spent(start);
        log("spent " + spent + " ms !");
    }

    // case1's Runnable
    static class Case01 implements Runnable {
        private MyInteger i;
        public Case01(MyInteger i) {
            this.i = i;
        }

        @Override
        public void run() {
            for(int i=0; i<INC_NUM; i++) {
                this.i.i ++;
            }
        }
    }
    // case2's Runnable
    static class Case02 implements Runnable {
        private MyInteger i;
        public Case02(MyInteger i) {
            this.i = i;
        }

        @Override
        public void run() {
            if(syncOuter) {
                synchronized (Case02.class) {
                    for(int i=0; i<INC_NUM; i++) {
                        this.i.i ++;
                    }
                }
            } else {
                for(int i=0; i<INC_NUM; i++) {
                    synchronized (Case02.class) {
                        this.i.i ++;
                    }
                }               
            }
        }
    }
    // case2Lock's Runnable
    static class Case02Lock implements Runnable {
        private MyInteger i;
        private static Lock lock = new ReentrantLock();
        public Case02Lock(MyInteger i) {
            this.i = i;
        }

        @Override
        public void run() {
            if(syncOuter) {
                lock.lock();
                try {
                    for(int i=0; i<INC_NUM; i++) {
                        this.i.i ++;
                    }
                } finally {
                    lock.unlock();
                }
            } else {
                for(int i=0; i<INC_NUM; i++) {
                    lock.lock();
                    try {
                        this.i.i ++;
                    } finally {
                        lock.unlock();
                    }
                }               
            }
        }
    }
    // case3's Runnable
    static class Case03 implements Runnable {
        private AtomicInteger i;
        public Case03(AtomicInteger i) {
            this.i = i;
        }

        @Override
        public void run() {
            for(int i=0; i<INC_NUM; i++) {
                this.i.incrementAndGet();
            }
        }
    }

    // MyInteger
    static class MyInteger {
        public int i;
        public MyInteger(int i) {
            this.i = i;
        }
    }

}

测试结果

测试环境 : win7 + jdk1.7.40[64bit][serverVM]
以下结果依次为线程数为1-10的结果
syncOuter为true的时候, Lock, synchronized的开销差不多[有时候 甚至于synchronized的开销比思路1的开销还小呢]

-----------------------------1----------------------------------
spent 7 ms !
spent 69 ms !
spent 88 ms !
spent 37 ms !
1000000,  1000000,  1000000,  1000000
-----------------------------2----------------------------------
spent 9 ms !
spent 344 ms !
spent 405 ms !
spent 261 ms !
1011059,  2000000,  2000000,  2000000
-----------------------------3----------------------------------
spent 12 ms !
spent 541 ms !
spent 373 ms !
spent 843 ms !
1995970,  3000000,  3000000,  3000000
-----------------------------4----------------------------------
spent 11 ms !
spent 775 ms !
spent 427 ms !
spent 1207 ms !
1999023,  4000000,  4000000,  4000000
-----------------------------5----------------------------------
spent 13 ms !
spent 877 ms !
spent 519 ms !
spent 1768 ms !
3005573,  5000000,  5000000,  5000000
-----------------------------6----------------------------------
spent 14 ms !
spent 1153 ms !
spent 628 ms !
spent 2142 ms !
2861138,  6000000,  6000000,  6000000
-----------------------------7----------------------------------
spent 14 ms !
spent 1346 ms !
spent 691 ms !
spent 2398 ms !
3911842,  7000000,  7000000,  7000000
-----------------------------8----------------------------------
spent 16 ms !
spent 1533 ms !
spent 809 ms !
spent 3032 ms !
4608651,  8000000,  8000000,  8000000
-----------------------------9----------------------------------
spent 15 ms !
spent 1771 ms !
spent 883 ms !
spent 3713 ms !
3348381,  9000000,  9000000,  9000000
-----------------------------10----------------------------------
spent 14 ms !
spent 1909 ms !
spent 976 ms !
spent 4052 ms !
4633594,  10000000,  10000000,  10000000

1 开销上面的差别

关键的地方在于, 效率上面的差别, 为何差这么多[请见之后的结果], 记得曾经在某本虚拟机方面书上面看过synchronized是比较重量级的, jdk1.5的时候增加了并发包相关的api, 可以使用Lock相关api[ReentryLock, StampedLock[jdk1.8] ], 然后在jdk1.6的时候, 似乎是对于synchronized做了优化 缩小了其余Lock相关api的差距

第一种思路 这里就不说了, 不能保证正确性
按照我的思考, synchronized应该是最慢的吧, 然后是Lock相关api, 然后是Atomic相关api
谁知道测试下来,, 与我所想的差距也太大了吧,, 在线程数为1, 2的时候Atomic相关api最快, 其他两个其次

当线程数为3-10的时候, Lock相关api最快, synchronized差不多是前者的两倍, AtomicInteger差不多是synchronized的两倍


对于AtomicInteger的思路
    为何会有这么大的差距呢, 我的机器4核, 假设同时两个cpu跑了两个线程, 假设发生四次碰撞更新成功一次i, 那么 也不应该开销这么大啊,,

还是先占一个位置, 等待以后补了脑再回来思考这些问题吧,,,
或者 路过的大大指点指点 +_+

总结

非常有意思的一个问题, 期待能够快点相同问题之所在

版权声明:本文为博主原创文章,未经博主允许不得转载。

java多线程分发问题——多线程求和

最近读了的分发部分,又碰到一个网友想利用多线程求和并且汇总求最后结果,具体要求是这样的“写十个线程,第一个线程求1到10的和,第二个11到20的和,第三个求21到30的和...第10个求91到100的...
  • u012816142
  • u012816142
  • 2015年10月23日 14:21
  • 1892

Java并发和多线程2:3种方式实现数组求和

本篇演示3个数组求和的例子。例子1:单线程例子2:多线程,同步求和(如果没有计算完成,会阻塞)例子3:多线程,异步求和(先累加已经完成的计算结果)例子1-代码package cn.fansunion....
  • FansUnion
  • FansUnion
  • 2015年12月30日 13:08
  • 4210

原子类与多线程中变量的累加问题

java并发包中的原子类的知识和多线程中对变量累加的同步操作
  • yicong406880638
  • yicong406880638
  • 2016年02月26日 16:58
  • 788

java多线程之从1数到10 //两个线程进行数数

军训时最常见的莫过于报数了,1、2、3、4、5..... 现在我要用java的多线程实现类似军训报数的功能, 即开启两个线程,让它们轮流数数,从1数到10,如: 线程A:1 线程B:2 线程...
  • friendan
  • friendan
  • 2014年01月04日 13:51
  • 3178

多线程累加程序

 #include stdio.h>#include pthread.h>#include sys/time.h>#include string.h>#define MAX 30pthread_t t...
  • jimlee070128
  • jimlee070128
  • 2007年10月11日 09:31
  • 550

一个求累加和程序的求解过程

【题目】求f=1-1/2!+1/3!-...+1/9! 【本文结构】   看题目,有点难度(相对而言,熟悉编程后,这只是一个非常典型的问题)。将我带着大家把这个问题分解一下,由易到难解出来...
  • sxhelijian
  • sxhelijian
  • 2012年02月29日 09:32
  • 7364

OpenCL多线程累加计算

初学OpenCL,写的不好的地方还请见谅。 本次说的是一个累加和的问题,给定一个一维数组A[n],然后计算B[n],其中,B[0]=A[0],B[1]=A[1]+B[0],B[2]=A[2]+B[1...
  • Code_Life_LiWan
  • Code_Life_LiWan
  • 2013年04月15日 09:56
  • 2620

OpenCL 第5课:向量相加

OpenCL程序分为两个部份,一部份是内核代码,负责具体算法。另一部份是主程序负责初始化OpenCL和准备数据。主程序加载内核代码,并按照即定方法进行运算。 内核代码可以写在主程序里面,也可以写在另...
  • szu030606
  • szu030606
  • 2014年04月21日 14:35
  • 1020

一道数学累加问题

写这篇文章是源于昨天去一家公司面试,面试官给我出的一道题目: 创建5个线程,实现整数0加到1000,然后输出结果。 咋一看,也没什么特别的地方,很简单的。就实现如下了: package multiPr...
  • u014694028
  • u014694028
  • 2015年10月13日 17:38
  • 474

11 多线程累加问题

前言今天 无聊闲想的时候, 突然想到了一下多线程相关的问题 1. 累增的问题, 也就是对于一个int多个[x]线程对其进行++操作, 每个线程操作N次     1.1 常规场景下面, 多线...
  • u011039332
  • u011039332
  • 2016年08月30日 20:06
  • 931
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:11 多线程累加问题
举报原因:
原因补充:

(最多只允许输入30个字)