精华内容
下载资源
问答
  • 多线程下内存分配方式的比较...当软件性能优化到一定程度之后,用vturn查看hotspots,将会发现malloc/delete会花费很高比例的时间,此时如果是多线程程序,频繁的lock将会是一个瓶颈,这里有一篇oracle的文章,很好的
     

    多线程下内存分配方式的比较(TCMalloc vs mt_alloc)

    分类: Libraries 837人阅读 评论(0) 收藏 举报
    当软件性能优化到一定程度之后,用vturn查看hotspots,将会发现malloc/delete会花费很高比例的时间,此时如果是多线程程序,频繁的lock将会是一个瓶颈,这里有一篇oracle的文章,很好的介绍了这样的情况http://www.oracle.com/technetwork/articles/servers-storage-dev/mem-alloc-1557798.html

    在linux平台,此时我们主要考虑到两种优化方案,各自都有很细节的文章可以参考
    1. google的MTMalloc (http://goog-perftools.sourceforge.net/doc/tcmalloc.html)

    2. gnu的__mt_alloc (http://gcc.gnu.org/onlinedocs/libstdc++/manual/bk01pt12ch32.html)


    下面我做了一个简单的测试程序
    1. // test app  
    2. void* vectormemorycost(void*){  
    3.     cout << "malloc test"<< endl;  
    4.     int blocksize = 512;  
    5.     for(int j=0;j<20; ++j){  
    6.         int sum=25000;  
    7.         char* p1[sum];  
    8.         for(int i=0; i<sum;++i){  
    9.             p1[i]=(char*)malloc(blocksize);  
    10.         }  
    11.   
    12.         for(int i=0; i<sum;++i){  
    13.             free(p1[i]);  
    14.         }  
    15.     }  
    16.   
    17.     return 0;  
    18. }  
    19.   
    20. void* mtallocateMemorycost(void*){  
    21.     cout << "mt alloc test"<< endl;  
    22.     typedef pod value_type;  
    23.     typedef __gnu_cxx::__mt_alloc<value_type> allocator_type;  
    24.     typedef __gnu_cxx::__pool_base::_Tune tune_type;  
    25.   
    26. ///_M_align(__align)  
    27. ///_M_max_bytes(__maxb)  
    28. ///_M_min_bin(__minbin),  
    29. ///_M_chunk_size(__chunk)  
    30. ///_M_max_threads(__maxthreads),  
    31. ///_M_freelist_headroom(__headroom)  
    32. ///_M_force_new(__force)  
    33.   
    34.     tune_type t_our(16, 510, 32, 5120, 20, 10, false);  
    35.     allocator_type a;);  
    36.     a._M_set_options(t_our);  
    37.     int blocksize = 512;  
    38. for(int j=0;j<20; ++j){  
    39.     int sum=25000;  
    40.     allocator_type::pointer p1[sum];  
    41.     for(int i=0; i<sum;++i){  
    42.     p1[i]=a.allocate(blocksize);  
    43.     }  
    44.   
    45.     for(int i=0; i<sum;++i){  
    46.     a.deallocate(p1[i], blocksize);  
    47.     }  
    48. }  
    49.     return 0;  
    50. }  
    51.   
    52.   
    53.   
    54.   
    55. typedef void*(*pFoo)(void*);  
    56.   
    57. void mtallocatePrivate(pFoo func){  
    58.     int i;  
    59.     int cores = 4;  
    60.     pthread_t threads[cores];  
    61.     /* Create threads to do the work. */  
    62.     for (i = 0; i < cores; ++i)  
    63.         // pthread_create (&(threads[i]), NULL, vectormemorycost, NULL);  
    64.         pthread_create (&(threads[i]), NULL, func, NULL);  
    65.   
    66.     /* Wait for all threads to finish. */  
    67.     for (i = 0; i < cores; ++i)  
    68.         pthread_join (threads[i], NULL);  
    69. }  
    70.   
    71. void mtallocate(){  
    72.     for(int i=0;i<5;++i){  
    73.         mtallocatePrivate(mtallocateMemorycost);  
    74.         mtallocatePrivate(vectormemorycost);  
    75.     }  
    76. }  




    改变的tune_type参数,发现gnu的最优的情况比malloc快一两倍,最糟糕的情况是反而比malloc慢十倍,很依赖具体的实现情况,另外tcmalloc使用很简单,我们只要改一下link的库,测试的结果是比不用tcmalloc有七八倍的性能提高,从结构上来讲gnu的在更高层次的代码上优化,想通过memory pool和thread id的比较来减少申请内存的次数和不同线程之间的竞争,它最终调用的还是malloc尤其是当申请的内存大于_M_max_bytes的时候就完全是调用malloc了,而TCMalloc是替换掉了系统的malloc,是更加底层的优化。

    总结一下,gnu的使用麻烦,接口繁琐,还可能有负面效应,google的比gnu的速度快,使用也方便很多。
    展开全文
  • 从以前流行面向过程编程到现在面向对象编程,面向对象编程极大提升了现代软件开发效率和规模,但是不可避免是对象之间相互切换,为了让程序维护,我们必须引入“高效并发”来保证并发正确性...

    JVM学习–(九)线程安全和锁优化

    一.概述

    从以前流行的面向过程编程到现在的面向对象编程,面向对象编程极大的提升了现代软件开发的效率和规模,但是不可避免的是对象之间的相互切换,为了让程序维护的更好更好,我们必须引入“高效并发”来保证并发的正确性、安全性和效率性。

    二.线程安全

    比较宽泛的定义(没有什么可以操作性):如果一个对象可以被多个线程同时使用,那么它就是线程安全的。
    比较严谨并且有操作性的定义:当多个线程同时访问同一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也需要进行额外的同步操作,或者在调用方法进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那么就称这个对象是线程安全的。

    Java语言中的线程安全

    为了深入理解,可以把线程安全当做一个非真即假的二元排它项,根据安全程度从强到弱可以分成以下五类:不可变、绝对线程安全、相对线程安全、线程兼容和线程对立。
    1.不可变
    不可变的对象一定是线程安全的,无论是对象的方法实现还是方法的调用者,都不需要再进行任何的线程安全保障措施。例如final关键词就可以达到这种效果。
    Java语言中如果多线程共享的数据是一个基本数据类型,那么定义时的final关键词就可以保证它的不变性,例如String以及Integer等都是通过final来保证不变性。
    2.绝对线程安全
    一个类要达到“不管运行时环境如何,调用者都不需要任何额外的同步措施”。Java的API中很多声明自己是线程安全的类大多数都不是绝对的线程安全。例如Vector中的get()、remove()方法都是synchronize的,但是不能保证顺序间也是原子性的。
    3.相对线程安全
    就是我们俗称的线程安全,相比于绝对线程安全,我们需要对于一些特定顺序的连续调用,我们需要调用额外的同步手段来保证调用的正确性。
    4.线程兼容
    指的是对象本身并不是线程安全的,但是可以通过在调用端正确地使用同步手段来保证对象在并发环境下可以安全地使用。
    5.线程对立
    指的是无论采取了什么同步措施,都无法在多线程环境中并发地使用代码。

    线程安全的实现方法

    实现代码线程安全和代码编写有很大的关系,但虚拟机提供的同步和锁机制也起到了至关重要的作用。
    1.互斥同步
    同步是指多个线程并发访问共享数据时,保证共享数据在同一时刻只被其中的一条线程所使用。互斥就是实现同步的一种手段,临界区、互斥量和信号量都是常见的互斥实现方式。因此互斥是因,同步是果;互斥是方法,同步是目的。
    Java中最基本的互斥同步方法就是synchronized关键词,编译之后在同步块的前后会形成monitorenter和monitorexit。在使用的时候需要注意:1️⃣被synchronize修饰的同步块对于同一块线程来说都是可重入的,意味着同一线程反复进入同步块也不会出现自己把自己锁死的情况。2️⃣被synchronize修饰的同步块在持有锁的线程执行完毕并释放锁之前,会无条件地阻塞后面的进程的进入。
    因此持有锁是一个重量级的操作。java的线程调换是通过内核线程来完成的,因此会陷入用户态和内核态的调用中,所以一般在不必要的场所会对其进行一定的优化操作。
    除了synchronize之外,jdk5之后还提供了J.U.C包,其中的Lock接口成为了java的另一种全新的互斥同步手段。重入锁是Lock接口最常见的一种实现,在用法上与synchronize类似,不过相比多增加了三个新的功能:
    1️⃣等待可中断:当持有锁的线程长期不释放的时候,正在等待的线程可以选择放弃,改做其他事情。
    2️⃣公平锁:多个线程等待的时候,必须按照申请锁的时间顺序来依次获得锁。
    3️⃣锁绑定多个条件:一个重入锁对象可以绑定多个Condition对象。如果和多于一个的条件关联的时候,可以直接添加Condition对象。
    在JDK6之后优化,synchronize和重入锁性能上已经基本持平。虽然reentrantLock的性能高于Synchronize,但是还是推荐使用Synchronize,原因:
    1️⃣synchronize更为简洁方便。
    2️⃣Lock必须手动释放锁,但是synchronized的话即使出了异常,系统会自动的释放。
    3️⃣虽然以前lock的性能优于synchronize,但是以后的优化都会优先考虑synchronize。

    2.非阻塞同步
    互斥同步的主要问题就是线程阻塞和唤醒的开销,因此也被称为“阻塞同步”,解决方案来看,这属于一种“悲观锁”:认为只要不去正确的做同步措施,那么肯定会出问题。随着硬件指令集的发展,我们已经有了另外一个选择:基于冲突检测的乐观并发策略,通俗来讲就是先运行,当出现故障再想方法补救,最常用的补偿方法是不断地重试,直到出现没有竞争的共享数据为止。这种乐观并发策略不再需要把线程阻塞挂起,因此这种同步操作被称为非阻塞同步。
    乐观并发策略需要“硬件指令集的发展”,因为我们必须要求操作和冲突检测这两个步骤具备原子性。我们只可以通过硬件来实现,硬件保证了从语义上看起来需要多次操作的行为可以通过一条处理器指令就能完成,这类指令常用的是:
    ①测试并设置②获取并增加③交换④比较并交换(CAS)⑤加载链接/条件储存(LL/SC)
    4,5两条是现代处理器新增的,这两条的目的和功能也是类似的。下面以CAS指令为例来进行分析:
    CAS指令需要三个操作数,分别是内存位置(V)、旧的预期值(A)以及准备设置的新值(B )。当V符合A时,处理器才会用B来更新V的值,否则就不执行更新操作。但是无论是否更新了V的值,都会返回V的旧值。jdk9之前只有java类库可以使用CAS,例如J.U.C包中的整数原子类。
    CAS并不能涵盖互斥同步的所有使用场景,例如它存在一个逻辑漏洞,称为“ABA”问题。

    3.无同步方案
    要保证线程安全并非一定需要进行阻塞或者阻塞同步,同步与线程安全之间并没有必要的联系。如果保证一个方法本来就不涉及共享数据,那么自然不需要任何同步措施来保证其确定性。因此有些代码天生就是安全的,例如:
    1️⃣可重入代码。又被称为纯代码,指的是代码执行的任何阶段中断它转而运行其他的代码,而在控制权返回后也不会对结果产生什么影响。所有的可重入代码都是线程安全的,但是并不是所有线程安全的代码都是可重入的。比较简单的判定规则:如果一个方法的返回结果是可以预测的,只要输入相同的数据,就能返回相同的结果,那么它就满足可重入的要求。
    2️⃣线程本地存储。如果一个线程中的数据必须和其他代码共享,那么就看看这些共享数据的代码是否能保证在同一个线程中执行。如果能够保证,就可以把共享数据的可见性放在一个线程中,这样就可以保证是线程安全的。

    三.锁优化

    JDK5到JDK6有一个重要的改进项,就是对锁进行了很多优化,这些技术都是为了提高程序的运行速度。

    自旋锁与自适应自旋

    很多时候锁定状态维持的时间很短,如果为了这段时间去挂起或者恢复线程十分不值得。可以通过让等待的锁执行一个忙循环(自旋)而不是将其阻塞,这就是自旋锁。
    JDK6中加入自适应的自旋锁,意味着自旋的时间不是固定的,虚拟机会根据运行情况来设定自旋次数的阈值。

    锁消除

    指的是虚拟机即时编译器在运行时,对一些代码要求同步,但是对被检测到不可能存在共享数据竞争的锁进行消除。主要判断依据是逃逸分析的数据支持。

    锁粗化

    原则上是要求同步块越小越好,但是一系列对于同一对象反复的加锁解锁的过程中,频繁的进行互斥同步操作会导致不必要的性能损耗。因此可以考虑将锁粗化。

    轻量级锁

    JDK6后加入的新型锁机制,“轻量级”是相对于使用操作系统互斥量的传统锁而言的。但是轻量级锁并不能代替重量级锁,设计的初衷是在没有多线程竞争的前提下,减少传统的重量级锁使用操作系统互斥量的性能消耗。
    hotspot虚拟机中对象头分为两部分,第一部分储存对象自身的运行时数据,例如哈希码、GC分代年龄等。这部分是实现轻量级锁和偏向锁的关键。另一部分用来储存指向方法区对象类型的指针。在32位的虚拟机中对象在没有被锁定的情况下,2个比特用来存储锁标志位,还有1个比特固定为0,除了未被锁定的情况,还有轻量级锁定、重量级锁定、GC标记等。
    工作过程:代码即将进入同步块的时候,如果这个同步对象没有被锁定(01状态),虚拟机首先将在当前线程的栈帧中建立一个名为锁记录的空间,用来存储锁对象目前的MarkWord的拷贝,然后虚拟机将CAS操作尝试把对象的MarkWord更新为LockRecord的指针,如果成功了就代表着线程拥有这个对象的锁,并且对象锁标志位转变为“00”,表示此对象处于轻量级锁定。如果更新操作失败了,就意味着至少存在一条线程和当前线程竞争取得该对象的锁。这时候比对对象的markword是否指向当前线程的栈帧,如果是那么线程已经拥有这个对象的锁;否则说明了这个锁已经被其他线程所占用了。
    需要注意的是如果两个线程以上争同一个锁,轻量级锁就不再有效,必须膨胀为重量级锁。
    解锁过程也是一样,通过CAS操作,如果对象的markword仍然指向线程的锁记录,就把对象当前的markword和线程复制的DisplacedMark替换回来。
    轻量级锁能够提升同步性能的依据是“对于绝大部分的锁,在整个同步周期中都是不存在竞争的”,如果不存在竞争,轻量级锁通过CAS操作成功避免了互斥量的开销,但是如果存在竞争,反而会比重量级锁更慢。

    偏向锁

    目的是消除数据在无竞争情况下的同步原语,进一步提高程序的运行性能。它的意思是这个锁会偏向于第一个获得它的过程,如果在接下来的执行过程中,该锁一直没有被其他的线程获取,则持有偏向锁的线程将永远不需要同步。

    展开全文
  • 其实这种多线程下载难点主要是下载任务分配 下,打个比方,一个文件某个部分应该给哪个线程下载?为了简单(另一方面是我不愿多想),所以分配算法也比较简单,直接分成一块块,然后每个线程下载一块。如果...

    因为公司不允许用fg之类的软件,所以就搞了这个东西来下载东西。程序比较简单,尚有多处地方没有优化。其实这种多线程下载的难点主要是下载任务的分配 下,打个比方,一个文件的某个部分应该给哪个线程下载?为了简单(另一方面是我不愿多想),所以分配算法也比较简单,直接分成一块块,然后每个线程下载一块。如果读者有留意Flashget之类的软件下载时的过程图的话,应该会发现它们的算法比这里的好很多。

    这里我用HttpURLConnection下载,你也可以用HttpClient或者自己实现一个Http协议(不过貌似没有必要)

    其次,你可能发现我这里效仿迅雷,一个任务生成两个文件,一个是任务描述文件,一个是真正的下载文件,而Flahget则是只有一个文件。在任务描述文件里,我把前4K用来做一些描述,然后之后的用于记录下载的过程。

    另外,这里写文件没有实现缓存写之类的功能,不过那些功能做起来不难。

    最后,希望你转载文章的时候,麻烦保留作者信息。(夏威夷雪人 or 书虫)

     

     

    //这个是任务Bean
    
    public class Task {
    
    
    
      private String downURL;
    
    
    
      private String saveFile;
    
      
    
      private int bufferSize = 64 * 1024;
    
      
    
      private int workerCount;
    
      
    
      private int sectionCount;
    
      
    
      private long contentLength;
    
      
    
      private long[] sectionsOffset;
    
      
    
      public static final int HEAD_SIZE = 4096;
    
      
    
      //读下载描述文件内容
    
      public synchronized void read(RandomAccessFile file) throws IOException {
    
        byte[] temp = new byte[HEAD_SIZE];
    
        file.seek(0);
    
        int readed = file.read(temp);
    
        if (readed != temp.length) {
    
          throw new RuntimeException();
    
        }
    
        ByteArrayInputStream bais = new ByteArrayInputStream(temp);
    
        DataInputStream dis = new DataInputStream(bais);
    
        downURL = dis.readUTF();
    
        saveFile = dis.readUTF();
    
        sectionCount = dis.readInt();
    
        contentLength = dis.readLong();
    
        
    
        sectionsOffset = new long[sectionCount];
    
        for (int i = 0; i < sectionCount; i++) {
    
          sectionsOffset[i] = file.readLong();
    
        }
    
      }
    
      
    
      //创建下载描述文件内容
    
      public synchronized void create(RandomAccessFile file) throws IOException {
    
        if (sectionCount != sectionsOffset.length) {
    
          throw new RuntimeException();
    
        }
    
        long len = HEAD_SIZE + 8 * sectionCount;
    
        file.setLength(len);
    
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
    
        DataOutputStream dos = new DataOutputStream(baos);
    
        dos.writeUTF(downURL);
    
        dos.writeUTF(saveFile);
    
        dos.writeInt(sectionCount);
    
        dos.writeLong(contentLength);
    
        byte[] src = baos.toByteArray();
    
        byte[] temp = new byte[HEAD_SIZE];
    
        System.arraycopy(src, 0, temp, 0, src.length);
    
        file.seek(0);
    
        file.write(temp);
    
        writeOffset(file);
    
      }
    
      
    
      //更新下载的过程
    
      public synchronized void writeOffset(RandomAccessFile file) throws IOException {
    
        if (sectionCount != sectionsOffset.length) {
    
          throw new RuntimeException();
    
        }
    
        file.seek(HEAD_SIZE);
    
        for (int i = 0; i < sectionsOffset.length; i++) {
    
          file.writeLong(sectionsOffset[i]);
    
        }
    
      }
    
      (下面是Getter、Setter)
    
    }
    
    
    
    //这个是下载主程序
    
    
    
    public class TaskAssign {
    
    
    
      public void work(Task task) throws IOException {
    
        File file = new File(task.getSaveFile());
    
        if (file.exists()) {
    
          return;
    
        }
    
        //这个是记录是否下载成功。我这里也没有增加失败回复、重试之类的工作。
    
        final AtomicBoolean success = new AtomicBoolean(true);
    
        //任务描述文件
    
        File taskFile = new File(task.getSaveFile() + ".r_task");
    
        //真正下载的文件
    
        File saveFile = new File(task.getSaveFile() + ".r_save");
    
        boolean taskFileExist = taskFile.exists();
    
        RandomAccessFile taskRandomFile = null;
    
        RandomAccessFile downRandomFile = null;
    
        try {
    
          taskRandomFile = new RandomAccessFile(taskFile, "rw");
    
          downRandomFile = new RandomAccessFile(saveFile, "rw");
    
          long rtnLen = getContentLength(task.getDownURL());
    
          if (!taskFileExist) {
    
            //如果文件不存在,就初始化任务文件和下载文件
    
            task.setContentLength(rtnLen);
    
            initTaskFile(taskRandomFile, task);
    
            downRandomFile.setLength(rtnLen);
    
          } else {
    
            //任务文件存在就读取任务文件
    
            task.read(taskRandomFile);
    
            if (task.getContentLength() != rtnLen) {
    
              throw new RuntimeException();
    
            }
    
          }
    
          int secCount = task.getSectionCount();
    
          //分配线程去下载,这里用到线程池
    
          ExecutorService es = Executors.newFixedThreadPool(task.getWorkerCount());
    
          for (int i = 0; i < secCount; i++) {
    
            final int j = i;
    
            final Task t = task;
    
            final RandomAccessFile f1 = taskRandomFile;
    
            final RandomAccessFile f2 = downRandomFile;
    
            es.execute(new Runnable() {
    
              public void run() {
    
                try {
    
                  down(f1, f2, t, j);
    
                } catch (IOException e) {
    
                  success.set(false);
    
                  e.printStackTrace(System.out);
    
                }
    
              }
    
            });
    
          }
    
          es.shutdown();
    
          try {
    
            es.awaitTermination(24 * 3600, TimeUnit.SECONDS);
    
          } catch (InterruptedException e) {
    
            e.printStackTrace();
    
          }
    
          taskRandomFile.close();
    
          taskRandomFile = null;
    
          downRandomFile.close();
    
          downRandomFile = null;
    
          //如果下载成功,去掉任务描述文件、帮下载文件改名
    
          if (success.get()) {
    
            taskFile.delete();
    
            saveFile.renameTo(file);
    
          }
    
        } finally {
    
          if (taskRandomFile != null) {
    
            taskRandomFile.close();
    
            taskRandomFile = null;
    
          }
    
          if (downRandomFile != null) {
    
            downRandomFile.close();
    
            downRandomFile = null;
    
          }
    
        }
    
      }
    
      
    
      public void down(RandomAccessFile taskRandomFile, RandomAccessFile downRandomFile, Task task, int sectionNo) throws IOException {
    
        //这里我用HttpURLConnection下载,你也可以用HttpClient或者自己实现一个Http协议(不过貌似没有必要)
    
        URL u = new URL(task.getDownURL());
    
        HttpURLConnection conn = (HttpURLConnection) u.openConnection();
    
        long start = task.getSectionsOffset()[sectionNo];
    
        long end = -1;
    
        //这里要注意一下,这里是计算当前块的长度
    
        if (sectionNo < task.getSectionCount() - 1) {
    
          long per = task.getContentLength() / task.getSectionCount();
    
          end = per * (sectionNo + 1);
    
        } else {
    
          end = task.getContentLength();
    
        }
    
        if (start >= end) {
    
          System.out.println("Section has finished before. " + sectionNo);
    
          return;
    
        }
    
        String range = "bytes=" + start + "-" + (end - 1);
    
        conn.setRequestProperty("Range", range);
    
        conn.setRequestProperty("User-Agent", "Ray-Downer");
    
        try {
    
          conn.connect();
    
          if (conn.getResponseCode() != 206) {
    
            throw new RuntimeException();
    
          }
    
          if (conn.getContentLength() != (end - start)) {
    
            throw new RuntimeException();
    
          }
    
          InputStream is = conn.getInputStream();
    
          byte[] temp = new byte[task.getBufferSize()];
    
          BufferedInputStream bis = new BufferedInputStream(is, temp.length);
    
          int readed = 0;
    
          while ((readed = bis.read(temp)) > 0) {
    
            long offset = task.getSectionsOffset()[sectionNo];
    
            synchronized (task) {
    
              //下载之后顺便更新描述文件,你可能会发现这里效率比较低,在一个线程同步里进行两次文件操作。你可以自己实现一个缓冲写。
    
              downRandomFile.seek(offset);
    
              downRandomFile.write(temp, 0, readed);
    
              offset += readed;
    
              task.getSectionsOffset()[sectionNo] = offset;
    
              task.writeOffset(taskRandomFile);
    
            }
    
          }
    
        } finally {
    
          conn.disconnect();
    
        }
    
        System.out.println("Section finished. " + sectionNo);
    
      }
    
      
    
      public void initTaskFile(RandomAccessFile taskRandomFile, Task task) throws IOException {
    
        int secCount = task.getSectionCount();
    
        long per = task.getContentLength() / secCount;
    
        long[] sectionsOffset = new long[secCount];
    
        for (int i = 0; i < secCount; i++) {
    
          sectionsOffset[i] = per * i;
    
        }
    
        task.setSectionsOffset(sectionsOffset);
    
        task.create(taskRandomFile);
    
      }
    
      
    
      public long getContentLength(String url) throws IOException {
    
        URL u = new URL(url);
    
        HttpURLConnection conn = (HttpURLConnection) u.openConnection();
    
        try {
    
          return conn.getContentLength();
    
        } finally {
    
          conn.disconnect();
    
        }
    
      }
    
    }
    
    
    
    //稍微测试一下。
    
    public class Main {
    
      
    
      public static void main(String[] args) throws IOException {
    
        test3();
    
        System.out.println("\n\n===============\nFinished.");
    
      }
    
     
    
      public static void test1() throws IOException {
    
        Task task = new Task();
    
        task.setDownURL("http://61.152.235.21/qqfile/qq/2007iistable/QQ2007IIKB1.exe");
    
        task.setSaveFile("H:/Test2.exe");
    
        task.setSectionCount(200);
    
        task.setWorkerCount(100);
    
        task.setBufferSize(256 * 1024);
    
        TaskAssign ta = new TaskAssign();
    
        ta.work(task);
    
      }
    
      
    
      public static void test2() throws IOException {
    
        Task task = new Task();
    
        task.setDownURL("http://student1.scut.edu.cn:8880/manage/news/data/1208421861893.xls");
    
        task.setSaveFile("H:/Test3.xls");
    
        task.setSectionCount(5);
    
        task.setWorkerCount(1);
    
        task.setBufferSize(128 * 1024);
    
        TaskAssign ta = new TaskAssign();
    
        ta.work(task);
    
      }
    
      
    
      public static void test3() throws IOException {
    
        Task task = new Task();
    
        task.setDownURL("http://go.microsoft.com/fwlink/?linkid=57034");
    
        task.setSaveFile("H:/vc2005express.iso");
    
        task.setSectionCount(500);
    
        task.setWorkerCount(200);
    
        task.setBufferSize(128 * 1024);
    
        TaskAssign ta = new TaskAssign();
    
        ta.work(task);
    
      }
    
      
    
      public static void test4() throws IOException {
    
        Task task = new Task();
    
        task.setDownURL("http://down.sandai.net/Thunder5.7.9.472.exe");
    
        task.setSaveFile("H:/Thunder.exe");
    
        task.setSectionCount(30);
    
        task.setWorkerCount(30);
    
        task.setBufferSize(128 * 1024);
    
        TaskAssign ta = new TaskAssign();
    
        ta.work(task);
    
      }
    
    }
    
    
    
    
     
    展开全文
  • Android 启动优化

    2019-04-12 12:41:00
    对与Android项目来说,app启动速度是非常重要。因为用户打开你app给别人第一体验就是打开软件的速度。但是app启动速度是比较难以缩短,因为...第一:使用多线程充分利用cup运行效率提高运行...

    对与Android的项目来说,app的启动速度是非常重要的。因为用户打开你的app给别人的第一体验就是打开软件的速度。但是app的启动速度是比较难以缩短的,因为一般来说开发者在app的启动入口都会创建一些比较耗时的操作,比方说使用SharedPreferences,初始化一些第三方的类库还有进行网络请求的判断。关于启动的优化可以分为两个方向来进行。第一:使用多线程充分利用好cup的运行效率提高运行时的速度;第二:使用延迟加载的方式将不重要的一些耗时的操作放到界面显示之后在做。

     

    第一点使用异步方法加快启动时的速度。首先还是重写Application方法在onCreate方法里面初始化,然后建立一个手机核数对于的线程池,在线程池里面异步的加载耗时的操作。这样就可以进行异步的加载方式

        //手机的核数
        final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
        final int CORE_POOL_SIZE = Math.max(2,Math.min(CPU_COUNT-1,4));  
    //优化启动方式
            //异步方式创建线程池
            ExecutorService service = Executors.newFixedThreadPool(CORE_POOL_SIZE);
    
            //将初始化操作放到线程池里面去
            service.submit(new Runnable() {
                @Override
                public void run() {
                    initA();
                }
            });
    
            service.submit(new Runnable() {
                @Override
                public void run() {
                    initB();
                }
            });
    
            service.submit(new Runnable() {
                @Override
                public void run() {
                    initC();
                }
            });  

    但是这样的加载方式还是存在者问题,由于这些都是异步加载的内容所有不知道这些初始化到底什么时候完成,所以可能还是需要进行一些处理。比方说我们的App一开始就需要这个初始化的数据但是在主线程调用的时候并没有初始化完,在这个时候就可能产生异常,有的必须在Application里面初始化成功才有作用。所以可以使用CountDownLatch。

    首先初始化一个CountDownLatch后,在异步进程的最后调用await方法等待里面的进程countDown,这样就可以保证我们想要的进程一定可以执行完

    private CountDownLatch mCountDownLatch = new CountDownLatch(1);
    mCountDownLatch.countDown();
    mCountDownLatch.await();
           
        private CountDownLatch mCountDownLatch = new CountDownLatch(1);
        //手机的核数
        final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
        final int CORE_POOL_SIZE = Math.max(2,Math.min(CPU_COUNT-1,4));
    
    
    onCreate里面的方法 
     //异步方式创建线程池
            ExecutorService service = Executors.newFixedThreadPool(CORE_POOL_SIZE);
    
            //将初始化操作放到线程池里面去
            service.submit(new Runnable() {
                @Override
                public void run() {
                    //初始化的方法
                    initA();
                    mCountDownLatch.countDown();
                }
            });
    
            service.submit(new Runnable() {
                @Override
                public void run() {
                    initB();
                }
            });
    
            service.submit(new Runnable() {
                @Override
                public void run() {
                    initC();
                }
            });
    
            try {
                //等待完成之后就会完成的
                mCountDownLatch.await();
            }catch (Exception e){
                e.printStackTrace();
            }        
    

      如上面的代码所示除非initA方法执行完毕之后才会继续向下执行。

     

    第二点使用延迟加载的方式将不重要的一些耗时的操作放到界面显示之后在做

    我们可以先观察把一些一开始非必要的内容在MainActivity显示后进行初始化,在界面成功显示之后使用Handler进行延迟的初始化。


    new Handler().postDelayed(new Runnable(){

    public void run() {
    //进行初始化
    }
    }, 2000);

     

    转载于:https://www.cnblogs.com/kkrs/p/10695010.html

    展开全文
  • 软件工程知识点

    2012-12-02 21:34:25
    其具有非常好的工作稳定性和安全保密性。但系统建设费用、运行费用比较高,灵活性不够好,结构不便于扩充。 (2)客户机/服务器结构 客户机/服务器结构依靠网络将计算任务分布到许多台不同的计算机上,但通过其中的...
  • §6.1.1 系统设计阶段和开发阶段的优化 85 §6.1.2 改善产品系统的优化 85 §6.2 优化的优先步骤 86 §6.2.1 步骤1:优化商业规则 86 §6.2.2 步骤2:优化数据设计 87 §6.2.3 步骤3:优化应用程序设计 87 §6.2.4 ...
  • 若你作为一名管理员刚加入M公司,你认为快速原型法对公司的软件开发更加优越,请向公司副总裁写一份报告阐明你的理由,切记:副总裁不喜欢报告长度 超过一页(B5)。 参考答案提示: 应先简述瀑布模型的缺点,它已不...
  • 与cgi区别在于servlet处于服务器进程中,它通过多线程方式运行其service方法,一个实例可以服务于多个请求,并且其实例一般不会销毁,而CGI对每个请求都产生新进程,服务完成后就销毁,所以效率上低于servlet。...
  • 而且,该多线程IDE还意味着出现编程错误时不会中止:您在任何时间都可以中断执行或保存您工作。  易于安装——不同于SQL*Net,无需中间件,也无需数据库对象安装。只需点击安装程序按钮,您就可以开始安装从而...
  •  Kevin Kline是Quest软件公司SQL Server解决方案技术战略经理。Kevin从2004年开始就是微软SQL Server MVP,他是国际SQLServer专家联盟(PASS)创辽委员会成员和下一届总裁。他独自写作或参与合著了几本书,...
  • 现代多核电脑提供了多线程低层次代码优化,从而在最好性能下最快地恢复最复杂密码PDF文件解密完美方案 ////////////////////////////////////////////////// 支持PDF文件限制直接解除 支持PDF文件密码暴力...
  • 事务处理原理 第2版

    热门讨论 2012-12-30 10:49:38
    应用程序编程人员希望不受运行事务处理系统所要求众多复杂不同类型技术(如事务协议、消息协议、事务远程过程调用、持久性队列、多线程进程、资源池、会话管理和复制协议)影响。应用程序编程人员工作是理解...
  • FiberTaskingLib:基于任务的多线程库,支持具有任意依赖关系任务图表。 HPX:适用于任何规模并行分布式应用通用C++运行时系统。 Intel Games Task Scheduler:为了游戏开发者需要而设计任务调度框架。 ...
  • 第三卷共31章,各章之后附有很好的习题。本书可供计算机和通信专业的研究生、高年级本科生作为教科书和学习参考书,也可供从事科研和技术开发的人员参考。   内容简介  本书是一部计算机网络经典性教科书。...
  • java 面试题 总结

    2009-09-16 08:45:34
    与cgi区别在于servlet处于服务器进程中,它通过多线程方式运行其service方法,一个实例可以服务于多个请求,并且其实例一般不会销毁,而CGI对每个请求都产生新进程,服务完成后就销毁,所以效率上低于servlet。...
  • 说明:目前,数据分析是一个非常热门的方向,因为不管是互联网行业还是传统行业都已经积累了大量的数据,现在需要的就是从这些数据中提取有价值的信息,以便打造更好的产品或者为将来的决策提供支持。 给初学者的几...
  • asp.net知识库

    2015-06-18 08:45:45
    .NET反射在软件设计上应用 关于跨程序集反射 实现C#和VB.net之间相互转换 深入剖析ASP.NET组件设计]一书第三章关于ASP.NET运行原理讲述补白 asp.net 运行机制初探(httpModule加载) 利用反射来查看对象中...
  • 软件能快速扫描域名注册情况,批量导出导入,速度快,站长朋友不可多得的好工具,如今查找已备案域名比较困难,此工具配合已备案域名查询网】效果极佳。 版本介绍: ===========================================...
  • TCP超时计算是RTOx2,这样连续丢三次包就变成RTOx8了,十分恐怖,而KCP启动快速模式后不x2,只是x1.5(实验证明1.5这个值相对比较好),提高了传输速度。 选择性重传 vs 全部重传: TCP丢包时会全部重传从丢那个...
  • 第二代数字杂志虽则将文件“装订”到了一起,看上去不再七零八落,但差不多都是一幅静态的、僵死的面孔,且需要安装专门的软件或将杂志下载到本地后才能阅读。随后出现的第三代数字杂志技术,也就是通常被称作...
  • 敖丙稳住了多线程翻车现场 进程和线程基础知识全家桶,30 张图一套带走 20 张图揭开内存管理迷雾 看完这篇操作系统,和面试官扯皮就没问题了。 进程间通信 进程之间究竟有哪些通信方式? 键盘敲入 A 字母时,...
  • 50、多线程有几种实现方法?同步有几种实现方法? 33 51、启动一个线程是用run()还是start()? . 33 52、当一个线程进入一个对象一个synchronized方法后,其它线程是否可进入此对象其它方法? 33 53、线程基本概念...
  • 简单地说,Java具有如下特点:简单、面向对象、平台无关、多线程、分布式、安全、 晑性能、可靠、解释型、自动垃圾回收等特点。 这里只解释一下平台无关和分布式,其余在后面会逐步接触到 3:平台无关 所谓平台...
  • arrow:更好的 Python 日期时间操作类库。 Chronyk:Python 3 的类库,用于解析手写格式的时间和日期。 dateutil:Python datetime 模块的扩展。 delorean:解决 Python 中有关日期处理的棘手问题的库。 maya:...
  • 但是要记住学习算法最关键还是解题思路和方法,用什么语言实现是其次,如果你时间比较多我是建议你用 Java 语言再实现一遍。 《labuladong算法小抄》 非常推荐!这是一本很新书,写书前作者在 Github 开源...
  • LuceneInAction(第2版)_中文版

    千次下载 热门讨论 2012-07-12 09:52:59
    《Lucene实战(第2版)》对于从事搜索引擎工作的工程技术人员,以及在Java平台上进行各类软件开发的人员和编程爱好者,也具有很好的学习参考价值。 作者简介 作者:(美)麦肯德利斯 目录 第1部分 Lucene核心 第1章 ...
  • java源码包---java 源码 大量 实例

    千次下载 热门讨论 2013-04-18 23:15:26
     Java局域网通信——飞鸽传书源代码,大家都知道VB版、VC版还有Delphi版飞鸽传书软件,但是Java版确实不,因此这个Java文件传输实例不可错过,Java网络编程技能提升很有帮助。 Java聊天程序,包括服务端和...
  • Android UI组件实例集合

    热门讨论 2012-09-11 11:31:29
    我们这个项目的目标是收集很可能在个项目中都有用的想法(而且很可能已经被其他独立程序员实现了),定义一批比较合理且扩展性比较好的一批intents和接口,提供基础但稳定有效的实现,可以被其他Android程序所应用...
  • C#微软培训教材(高清PDF)

    千次下载 热门讨论 2009-07-30 08:51:17
    本书着重介绍语言本身,比较少涉及应用,不错入门书,从头讲起,不怕不明白。 <<page 1>> page begin==================== 目 目目 目 录 录录 录 第一部分 C#语言概述.4 第一章 第一章第一章 第...

空空如也

空空如也

1 2 3 4
收藏数 66
精华内容 26
关键字:

多线程优化比较好的软件