当前位置:  编程技术>移动开发
本页文章导读:
    ▪怎么充分利用多核CPU,计算很大的List中所有整数的和        如何充分利用多核CPU,计算很大的List中所有整数的和 引用 前几天在网上看到一个淘宝的面试题:有一个很大的整数list,需要求这个list中所有整数的和,写一个可以充分利用多核CPU的代码.........
    ▪ 治理你的线程池(Executor)        管理你的线程池(Executor)    我们都知道使用线程池能够控制线程的数量,尤其是大量的“短命”线程存在时,线程池将大大降低系统消耗(内存和CPU)。不过,线程池也同样需要管理,.........
    ▪ AlarmManager的应用       AlarmManager的使用 AlarmManager的使用机制有的称呼为全局定时器,有的称呼为闹钟。通过对它的使用,个人觉得叫全局定时器比较合适,其实它的作用和Timer有点相似。都有两种相似的用法:(1.........

[1]怎么充分利用多核CPU,计算很大的List中所有整数的和
    来源: 互联网  发布时间: 2014-02-18
如何充分利用多核CPU,计算很大的List中所有整数的和

引用
前几天在网上看到一个淘宝的面试题:有一个很大的整数list,需要求这个list中所有整数的和,写一个可以充分利用多核CPU的代码,来计算结果。

一:分析题目 
从题中可以看到“很大的List”以及“充分利用多核CPU”,这就已经充分告诉我们要采用多线程(任务)进行编写。具体怎么做呢?大概的思路就是分割List,每一小块的List采用一个线程(任务)进行计算其和,最后等待所有的线程(任务)都执行完后就可得到这个“很大的List”中所有整数的和。 

二:具体分析和技术方案 
既然我们已经决定采用多线程(任务),并且还要分割List,每一小块的List采用一个线程(任务)进行计算其和,那么我们必须要等待所有的线程(任务)完成之后才能得到正确的结果,那么怎么才能保证“等待所有的线程(任务)完成之后输出结果呢”?这就要靠java.util.concurrent包中的CyclicBarrier类了。它是一个同步辅助类,它允许一组线程(任务)互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程(任务)的程序中,这些线程(任务)必须不时地互相等待,此时 CyclicBarrier 很有用。简单的概括其适应场景就是:当一组线程(任务)并发的执行一件工作的时候,必须等待所有的线程(任务)都完成时才能进行下一个步骤。具体技术方案步骤如下: 
  • 分割List,根据采用的线程(任务)数平均分配,即list.size()/threadCounts。
  • 定义一个记录“很大List”中所有整数和的变量sum,采用一个线程(任务)处理一个分割后的子List,计算子List中所有整数和(subSum),然后把和(subSum)累加到sum上。
  • 等待所有线程(任务)完成后输出总和(sum)的值。

示意图如下: 


 
三:详细编码实现 
代码中有很详细的注释,这里就不解释了。
/** 
 * 计算List中所有整数的和<br> 
 * 采用多线程,分割List计算 
 * @author 飞雪无情 
 * @since 2010-7-12 
 */  
public class CountListIntegerSum {  
    private long sum;//存放整数的和  
    private CyclicBarrier barrier;//障栅集合点(同步器)  
    private List<Integer> list;//整数集合List  
    private int threadCounts;//使用的线程数  
    public CountListIntegerSum(List<Integer> list,int threadCounts) {  
        this.list=list;  
        this.threadCounts=threadCounts;  
    }  
    /** 
     * 获取List中所有整数的和 
     * @return 
     */  
    public long getIntegerSum(){  
        ExecutorService exec=Executors.newFixedThreadPool(threadCounts);  
        int len=list.size()/threadCounts;//平均分割List  
        //List中的数量没有线程数多(很少存在)  
        if(len==0){  
            threadCounts=list.size();//采用一个线程处理List中的一个元素  
            len=list.size()/threadCounts;//重新平均分割List  
        }  
        barrier=new CyclicBarrier(threadCounts+1);  
        for(int i=0;i<threadCounts;i++){  
            //创建线程任务  
            if(i==threadCounts-1){//最后一个线程承担剩下的所有元素的计算  
                exec.execute(new SubIntegerSumTask(list.subList(i*len,list.size())));  
            }else{  
                exec.execute(new SubIntegerSumTask(list.subList(i*len, len*(i+1)>list.size()?list.size():len*(i+1))));  
            }  
        }  
        try {  
            barrier.await();//关键,使该线程在障栅处等待,直到所有的线程都到达障栅处  
        } catch (InterruptedException e) {  
            System.out.println(Thread.currentThread().getName()+":Interrupted");  
        } catch (BrokenBarrierException e) {  
            System.out.println(Thread.currentThread().getName()+":BrokenBarrier");  
        }  
        exec.shutdown();  
        return sum;  
    }  
    /** 
     * 分割计算List整数和的线程任务 
     * @author lishuai 
     * 
     */  
    public class SubIntegerSumTask implements Runnable{  
        private List<Integer> subList;  
        public SubIntegerSumTask(List<Integer> subList) {  
            this.subList=subList;  
        }  
        public void run() {  
            long subSum=0L;  
            for (Integer i : subList) {  
                subSum += i;  
            }    
            synchronized(CountListIntegerSum.this){//在CountListIntegerSum对象上同步  
                sum+=subSum;  
            }  
            try {  
                barrier.await();//关键,使该线程在障栅处等待,直到所有的线程都到达障栅处  
            } catch (InterruptedException e) {  
                System.out.println(Thread.currentThread().getName()+":Interrupted");  
            } catch (BrokenBarrierException e) {  
                System.out.println(Thread.currentThread().getName()+":BrokenBarrier");  
            }  
            System.out.println("分配给线程:"+Thread.currentThread().getName()+"那一部分List的整数和为:\tSubSum:"+subSum);  
        }  
          
    }  
      
}  
 
有人可能对barrier=new CyclicBarrier(threadCounts+1);//创建的线程数和主线程main有点不解,不是采用的线程(任务)数是threadCounts个吗?怎么为CyclicBarrier设置的给定数量的线程参与者比我们要采用的线程数多一个呢?答案就是这个多出来的一个用于控制main主线程的,主线程也要等待,它要等待其他所有的线程完成才能输出sum值,这样才能保证sum值的正确性,如果main不等待的话,那么结果将是不可预料的。 
/** 
 * 计算List中所有整数的和测试类 
 * @author 飞雪无情 
 * @since 2010-7-12 
 */  
public class CountListIntegerSumMain {  
  
    /** 
     * @param args 
     */  
    public static void main(String[] args) {  
        List<Integer> list = new ArrayList<Integer>();  
        int threadCounts = 10;//采用的线程数  
        //生成的List数据  
        for (int i = 1; i <= 1000000; i++) {  
            list.add(i);  
        }  
        CountListIntegerSum countListIntegerSum=new CountListIntegerSum(list,threadCounts);  
        long sum=countListIntegerSum.getIntegerSum();  
        System.out.println("List中所有整数的和为:"+sum);  
    }  
  
}  
 四:总结 

 

本文主要通过一个淘宝的面试题为引子,介绍了并发的一点小知识,主要是介绍通过CyclicBarrier同步辅助器辅助多个并发任务共同完成一件工作。Java SE5的java.util.concurrent引入了大量的设计来解决并发问题,使用它们有助于我们编写更加简单而健壮的并发程序。 

附mathfox提到的ExecutorService.invokeAll()方法的实现 
这个不用自己控制等待,invokeAll执行给定的任务,当所有任务完成时,返回保持任务状态和结果的 Future 列表。sdh5724也说用了同步,性能不好。这个去掉了同步,根据返回结果的 Future 列表相加就得到总和了。

 

/** 
 * 使用ExecutorService的invokeAll方法计算 
 * @author 飞雪无情 
 * 
 */  
public class CountSumWithCallable {  
  
    /** 
     * @param args 
     * @throws InterruptedException  
     * @throws ExecutionException  
     */  
    public static void main(String[] args) throws InterruptedException, ExecutionException {  
        int threadCounts =19;//使用的线程数  
        long sum=0;  
        ExecutorService exec=Executors.newFixedThreadPool(threadCounts);  
        List<Callable<Long>> callList=new ArrayList<Callable<Long>>();  
        //生成很大的List  
        List<Integer> list = new ArrayList<Integer>();  
        for (int i = 0; i <= 1000000; i++) {  
            list.add(i);  
        }  
        int len=list.size()/threadCounts;//平均分割List  
        //List中的数量没有线程数多(很少存在)  
        if(len==0){  
            threadCounts=list.size();//采用一个线程处理List中的一个元素  
            len=list.size()/threadCounts;//重新平均分割List  
        }  
        for(int i=0;i<threadCounts;i++){  
            final List<Integer> subList;  
            if(i==threadCounts-1){  
                subList=list.subList(i*len,list.size());  
            }else{  
                subList=list.subList(i*len, len*(i+1)>list.size()?list.size():len*(i+1));  
            }  
            //采用匿名内部类实现  
            callList.add(new Callable<Long>(){  
                public Long call() throws Exception {  
                    long subSum=0L;  
                    for(Integer i:subList){  
                        subSum+=i;  
                    }  
                    System.out.println("分配给线程:"+Thread.currentThread().getName()+"那一部分List的整数和为:\tSubSum:"+subSum);  
                    return subSum;  
                }  
            });  
        }  
        List<Future<Long>> futureList=exec.invokeAll(callList);  
        for(Future<Long> future:futureList){  
            sum+=future.get();  
        }  
        exec.shutdown();  
        System.out.println(sum);  
    }  
  
}  
 

一些感言 
这篇文章是昨天夜里11点多写好的,我当时是在网上看到了这个题目,就做了一下分析,写了实现代码,由于水平有限,难免有bug,这里感谢xifo等人的指正。这些帖子从发表到现在不到24小时的时间里创造了近9000的浏览次数,回复近100,这是我没有想到的,javaeye很久没这么疯狂过啦。这不是因为我的算法多好,而是因为这个题目、这篇帖子所体现出的意义。大家在看完这篇帖子后不光指正错误,还对方案进行了改进,关键是思考,人的思维是无穷的,只要我们善于发掘,善于思考,总能想出一些意想不到的方案。 

从算法看,或者从题目场景对比代码实现来看,或许不是一篇很好的帖子,但是我说这篇帖子是很有意义的,方案也是在很多场景适用,有时我们可以假设这不是计算和,而是把数据写到一个个的小文件里,或者是分割进行网络传输等等,都有一定的启发,特别是回帖中的讨论。 

单说一下回帖,我建议进来的人尽量看完所有的回帖,因为这里是很多人集思广益的精华,这里有他们分析问题,解决问题的思路,还有每个人提到的,想想为什么能用?为什么不能用?为什么好?为什么不好? 

 

 

摘自:http://www.iteye.com/topic/711162


    
[2] 治理你的线程池(Executor)
    来源: 互联网  发布时间: 2014-02-18
管理你的线程池(Executor)

   我们都知道使用线程池能够控制线程的数量,尤其是大量的“短命”线程存在时,线程池将大大降低系统消耗(内存和CPU)。不过,线程池也同样需要管理,于是我写了本篇。 
首先,我们来看看管理器的整个继承关系: 



 
    显而易见,有ThreadPoolExecutor和ScheduledThreadPoolExecutor两个实现类,当然Executor类里也有一些内部类实现了特定的功能(如class DelegatedScheduledExecutorService),我们也可以自己通过扩展这里所有的接口、抽象类、类来实现自己的特定功能,如继承ThreadPoolExecutor类,覆写beforeExecute(),让它在每个任务开始执行前执行某些操作,还有很多可扩展功能,有兴趣的朋友可以自己摸索。 
    你有两种方法创建上面管理器的实例: 
1、你可以用上面介绍的两个类的那这些类的实例的构造函数来创建管理器的实例,不过你要自己配置一些诸如池最大尺寸(maximumPoolSize )的参数。 
2、Executors提供各种创建上面的类的实例的方法,它默认一些参数的设置。我主要介绍 
这种方法中的newFixedThreadPool(int)和newCachedThreadPool() 

------------newFixedThreadPool(int)------------ 
     创建一个默认尺寸的池,它同时运行的线程数将是固定的,如果你要让它课同时运行的最大线程数大于初始设置的那个参数,可以调用setMaximumPoolSize()来设置额外的线程来并行处理更多的任务。 
     我们调用下面的方法来添加新的任务,到底Executors是如何处理的呢? 

Java代码  
  • public void execute(Runnable command) {  
  •         if (command == null)  
  •             throw new NullPointerException();  
  •         if(poolSize>=corePoolSize|| !addIfUnderCorePoolSize(command)) {  
  • //如果实时连接数小于corePoolSize,那么调用addIfUnderCorePoolSize()方法  
  •             if (runState == RUNNING && workQueue.offer(command)) {  
  •     //如果实时连接数大于了corePoolSize,那么将任务加进等待队列中。  
  •                 if (runState != RUNNING || poolSize == 0)  
  •     //在执行workQueue.offer(command)的过程中shutdown了,确保所有的已经提交任务能够成功执行完。  
  •                     ensureQueuedTaskHandled(command);  
  •             }  
  •             else if (!addIfUnderMaximumPoolSize(command))  
  •       
  •                 reject(command); // is shutdown or saturated  
  •         }  
  •     }  
  •   
  • 下面我们来看下poolSize>=corePoolSize为不同状态时两种执行方法:  
  •  private boolean addIfUnderCorePoolSize(Runnable firstTask) {  
  •     //首先获取本类所有同步方法的锁  
  •         Thread t = null;  
  •         final ReentrantLock mainLock = this.mainLock;  
  •         mainLock.lock();  
  •         try {  
  •             if (poolSize < corePoolSize && runState == RUNNING)  
  •                 t = addThread(firstTask);  
  •         } finally {  
  •             mainLock.unlock();  
  •         }  
  •         if (t == null)  
  •             return false;  
  •         t.start();  
  •         return true;  
  •     }  
  •    
  •  private boolean addIfUnderMaximumPoolSize(Runnable firstTask) {  
  •     //首先获取本类所有同步方法的锁  
  •      Thread t = null;  
  •         final ReentrantLock mainLock = this.mainLock;  
  •         mainLock.lock();  
  •         try {  
  •             if (poolSize < maximumPoolSize && runState == RUNNING)  
  •                 t = addThread(firstTask);  
  •         } finally {  
  •             mainLock.unlock();  
  •         }  
  •         if (t == null)  
  •             return false;  
  •         t.start();  
  •         return true;  
  •   
  •     }  
  • 几乎完全一样,估计author Doug Lea当初也是直接copy的吧。  
  • 这两个方法都调用了  
  • private Thread addThread(Runnable firstTask) {  
  •         Worker w = new Worker(firstTask);  
  •     //这里并没有区分maximumPoolSize 和corePoolSize   
  •     Thread t = threadFactory.newThread(w);  
  •         if (t != null) {  
  •             w.thread = t;  
  •             workers.add(w);//workers并没有尺寸的限制  
  •             int nt = ++poolSize;  
  •     //这一步维护一个管理器使用过程中的最大尺寸,没什么好说的。  
  •             if (nt > largestPoolSize)  
  •                 largestPoolSize = nt;  
  •         }  
  •         return t;  
  •     }  
  • 于是我认为发现管理器在对待aximumPoolSize 和corePoolSize 时根本没有什么区别,可是这是不正确的,至于为什么,大家可以自己去探索!  

  •       ThreadPoolExecutor类内部有一个: 
    private final HashSet<Worker> workers = new HashSet<Worker>(); 
    其中Worker类是ThreadPoolExecutor一个内部类,实现了Runable接口。在addIfUnderMaximumPoolSize()和addIfUnderCorePoolSize()两个方法中将任务添加进这个workers[]中,这个数组维护一个正在运行的任务组,这个数组中的一个元素对应一个正在运行的线程,如果一个线程以外死亡,数组中的元素没有被移除,管理器将自动创建一个新的线程继续从头开始执行刚刚那个以外死亡的数组对应的任务。 
           如此神奇?那是如何实现的? 
           很简单,ThreadPoolExecutor维护的线程的run方法都是在这个loop中的, 
    Java代码  
  • while (task != null || (task = getTask()) != null) {  
  •                     runTask(task);  
  •                     task = null;  
  •                 }  

  • 如果意外死亡,task=null不执行,重新判断条件的时候再次调用runTask(task);即,死亡的是runTask(task)方法内部的run()调用而已。 

          说到这里,大家应该明白了,管理器无非就是用BlockingQueue<Runnable> workQueue队列(注意这个队列是线程安全的,挺有意思)来缓冲多出来的任务,而总是有不大于maximumPoolSize(注意,这里不是corePoolSize )的线程在运行着,再有点异常死亡处理的能力而已。 


    --------newCachedThreadPool()-------- 

    Java代码  
  • 这个方法源码:  
  •  public static ExecutorService newCachedThreadPool() {  
  •         return new ThreadPoolExecutor(0, Integer.MAX_VALUE,  
  •                                       60L, TimeUnit.SECONDS,  
  •                                       new SynchronousQueue<Runnable>());  
  •     }  


  •     原来,就是让corePoolSize =0,maximumPoolSize=最大整数,然后设置空闲线程的存活时间为60s而已。看到这里,大家或许会冒出这样一个疑问:既然corePoolSize 是0,那么不是运行不了任何线程吗?呵呵,大家如果认真看了前面的文章就会有此疑问了。看同时刻运行的线程最大数是看参数maximumPoolSize不是corePoolSize 。 
    至于存活时间设置,那是很有必要,否则 
    Java代码  
  • while (task != null || (task = getTask()) != null) {  
  •                     runTask(task);  
  •                     task = null;  
  •                 }  

  • getTask方法中从待执行任务缓冲队列中poll()任务的时候会有一个存活时间的超时机制,如果超时将返回null,这个线程将因为一系列连锁反应,最终死亡。 

          好了,看似简单的Executor我砍了这么多,顺序整理的不是很好,大家将就看看吧。 
    总结一下,在设计这几个类的时候用到集合、同步(锁和阻塞队列)、枚举(TimeUnit)、多线程、安全控制(本文没有涉及)、工厂设计模式等等知识点,不简单哪^-^ 

     

     

    摘自:http://www.iteye.com/topic/842139


        
    [3] AlarmManager的应用
        来源: 互联网  发布时间: 2014-02-18
    AlarmManager的使用

    AlarmManager的使用机制有的称呼为全局定时器,有的称呼为闹钟。通过对它的使用,个人觉得叫全局定时器比较合适,其实它的作用和Timer有点相似。都有两种相似的用法:(1)在指定时长后执行某项操作(2)周期性的执行某项操作

    AlarmManager对象配合Intent使用,可以定时的开启一个Activity,发送一个BroadCast,或者开启一个Service.

    下面的代码详细的介绍了两种定时方式的使用:

     (1)在指定时长后执行某项操作

    代码 

     

           //操作:发送一个广播,广播接收后Toast提示定时操作完成
    Intent intent =new Intent(Main.this, alarmreceiver.class); intent.setAction("short"); PendingIntent sender= PendingIntent.getBroadcast(Main.this, 0, intent, 0); //设定一个五秒后的时间 Calendar calendar=Calendar.getInstance(); calendar.setTimeInMillis(System.currentTimeMillis()); calendar.add(Calendar.SECOND, 5); AlarmManager alarm=(AlarmManager)getSystemService(ALARM_SERVICE); alarm.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), sender); //或者以下面方式简化 //alarm.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()+5*1000, sender); Toast.makeText(Main.this, "五秒后alarm开启", Toast.LENGTH_LONG).show();

    //注意:receiver记得在manifest.xml注册

    代码
    public static class alarmreceiver extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub if(intent.getAction().equals("short")){ Toast.makeText(context, "short alarm", Toast.LENGTH_LONG).show(); }else{ Toast.makeText(context, "repeating alarm",
    Toast.LENGTH_LONG).show(); } } }

    (2)周期性的执行某项操作

    代码
    Intent intent =new Intent(Main.this, alarmreceiver.class); intent.setAction("repeating"); PendingIntent sender=PendingIntent .getBroadcast(Main.this, 0, intent, 0);
    //开始时间 long firstime=SystemClock.elapsedRealtime(); AlarmManager am=(AlarmManager)getSystemService(ALARM_SERVICE);
      //5秒一个周期,不停的发送广播 am.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP , firstime, 5*1000, sender);

     AlarmManager的setRepeating()相当于Timer的Schedule(task,delay,peroid);有点差异的地方时Timer这个方法是指定延迟多长时间

    以后开始周期性的执行task;

    AlarmManager的取消:(其中需要注意的是取消的Intent必须与启动Intent保持绝对一致才能支持取消AlarmManager)

    代码
    Intent intent =new Intent(Main.this, alarmreceiver.class); intent.setAction("repeating"); PendingIntent sender=PendingIntent .getBroadcast(Main.this, 0, intent, 0); AlarmManager alarm=(AlarmManager)getSystemService(ALARM_SERVICE); alarm.cancel(sender);

        
    最新技术文章:
    ▪Android开发之登录验证实例教程
    ▪Android开发之注册登录方法示例
    ▪Android获取手机SIM卡运营商信息的方法
    ▪Android实现将已发送的短信写入短信数据库的...
    ▪Android发送短信功能代码
    ▪Android根据电话号码获得联系人头像实例代码
    ▪Android中GPS定位的用法实例
    ▪Android实现退出时关闭所有Activity的方法
    ▪Android实现文件的分割和组装
    ▪Android录音应用实例教程
    ▪Android双击返回键退出程序的实现方法
    ▪Android实现侦听电池状态显示、电量及充电动...
    ▪Android获取当前已连接的wifi信号强度的方法
    ▪Android实现动态显示或隐藏密码输入框的内容
    ▪根据USER-AGENT判断手机类型并跳转到相应的app...
    ▪Android Touch事件分发过程详解
    ▪Android中实现为TextView添加多个可点击的文本
    ▪Android程序设计之AIDL实例详解
    ▪Android显式启动与隐式启动Activity的区别介绍
    ▪Android按钮单击事件的四种常用写法总结
    ▪Android消息处理机制Looper和Handler详解
    ▪Android实现Back功能代码片段总结
    ▪Android实用的代码片段 常用代码总结
    ▪Android实现弹出键盘的方法
    ▪Android中通过view方式获取当前Activity的屏幕截...
    ▪Android录音应用实例教程 iis7站长之家
    ▪Android提高之多方向抽屉实现方法
    ▪Android提高之MediaPlayer播放网络音频的实现方法...
    ▪Android提高之MediaPlayer播放网络视频的实现方法...
    ▪Android提高之手游转电视游戏的模拟操控
     


    站内导航:


    特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

    ©2012-2021,,E-mail:www_#163.com(请将#改为@)

    浙ICP备11055608号-3