一、什么是归并排序

归并排序又称合并排序,它是成功应用分治技术的一个完美例子。对于一个需要排序的数组A[0..n-1],归并排序把它一分为二:A[0..n/2-1]和A[n/2..n-1],并对每个子数组递归排序,然后把这两个排好序的子数组合并为一个有序数组。下面是归并排序的例子图解:

二、单线程实现归并排序

package com.bob.algorithms.sort;

import java.util.Arrays;

import com.bob.algorithms.SortStrategy;

/**
* 归并排序
*
* @author bob
*
*/
public class SingleThreadMergeSort implements SortStrategy { public int[] sort(int[] rawArray) {
mergeSort(rawArray);
return rawArray;
} /**
* 分解并合并排序,升序
*
* @param intArr
*/
private void mergeSort(int[] intArr) {
if (intArr.length > 1) {
// 如果数组长度大于1就分解称两份
int[] leftArray = Arrays.copyOfRange(intArr, 0, intArr.length / 2);
int[] rightArray = Arrays.copyOfRange(intArr, intArr.length / 2, intArr.length);
mergeSort(leftArray);
mergeSort(rightArray); // 合并且排序
merge(leftArray, rightArray, intArr);
}
} /**
* 合并排序
*
* @param leftArray
* @param rightArray
* @param intArr
*/
private void merge(int[] leftArray, int[] rightArray, int[] intArr) { // i:leftArray数组索引,j:rightArray数组索引,k:intArr数组索引
int i = 0, j = 0, k = 0;
while (i < leftArray.length && j < rightArray.length) {
// 当两个数组中都有值的时候,比较当前元素进行选择
if (leftArray[i] < rightArray[j]) {
intArr[k] = leftArray[i];
i++;
} else {
intArr[k] = rightArray[j];
j++;
}
k++;
} // 将还剩余元素没有遍历完的数组直接追加到intArr后面
if (i == leftArray.length) {
for (; j < rightArray.length; j++, k++) {
intArr[k] = rightArray[j];
}
} else {
for (; i < leftArray.length; i++, k++) {
intArr[k] = leftArray[i];
}
}
}
}

三、使用Fork/Join框架实现归并排序

Fork/Join是从JDK 1.7 加入的并发计算框架。

package com.bob.algorithms.sort;

import java.util.Arrays;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction; import com.bob.algorithms.SortStrategy; public class ForkJoinMergeSort implements SortStrategy { public int[] sort(int[] rawArray) {
ForkJoinPool pool = new ForkJoinPool();
pool.invoke(new MergeSort(rawArray));
return rawArray;
} /**
* 使用Fork/join的方式进行归并排序,充分利用cpu
*
* @author zhangwensha
*
*/
private static class MergeSort extends RecursiveAction { private static final long serialVersionUID = 425572392953885545L;
private int[] intArr; public MergeSort(int[] intArr) {
this.intArr = intArr;
} @Override
protected void compute() {
if (intArr.length > 1) {
// 如果数组长度大于1就分解称两份
int[] leftArray = Arrays.copyOfRange(intArr, 0, intArr.length / 2);
int[] rightArray = Arrays.copyOfRange(intArr, intArr.length / 2, intArr.length); // 这里分成两份执行
invokeAll(new MergeSort(leftArray), new MergeSort(rightArray)); // 合并且排序
merge(leftArray, rightArray, intArr);
}
} /**
* 合并排序
*
* @param leftArray
* @param rightArray
* @param intArr
*/
private void merge(int[] leftArray, int[] rightArray, int[] intArr) { // i:leftArray数组索引,j:rightArray数组索引,k:intArr数组索引
int i = 0, j = 0, k = 0;
while (i < leftArray.length && j < rightArray.length) {
// 当两个数组中都有值的时候,比较当前元素进行选择
if (leftArray[i] < rightArray[j]) {
intArr[k] = leftArray[i];
i++;
} else {
intArr[k] = rightArray[j];
j++;
}
k++;
} // 将还剩余元素没有遍历完的数组直接追加到intArr后面
if (i == leftArray.length) {
for (; j < rightArray.length; j++, k++) {
intArr[k] = rightArray[j];
}
} else {
for (; i < leftArray.length; i++, k++) {
intArr[k] = leftArray[i];
}
}
} }
}
 

四、单线程 pk 多线程

编写了舞台类,通过调整generateIntArray(10000000)的输入参数来设置待排序数组长度,试验中没有对堆容量进行设置。

package com.bob.algorithms;

import java.util.Arrays;
import java.util.Date; import com.bob.algorithms.common.CommonUtil;
import com.bob.algorithms.sort.ForkJoinMergeSort;
import com.bob.algorithms.sort.SingleThreadMergeSort; /**
* 舞台类,专门用来测试算法的时间
*
* @author bob
*
*/
public class Stage { public static void main(String[] args) { // 变量定义
long begintime = 0;
long endtime = 0; // 生成排序数据
int[] rawArr = generateIntArray(10000000);
int[] rawArr2 = Arrays.copyOf(rawArr, rawArr.length); begintime = new Date().getTime();
new SingleThreadMergeSort().sort(rawArr);
//System.out.println(Arrays.toString(new SingleThreadMergeSort().sort(rawArr)));
endtime = new Date().getTime();
System.out.println("单线程归并排序花费时间:" + (endtime - begintime));
System.out.println("是否升序:"+CommonUtil.isSorted(rawArr, true)); begintime = new Date().getTime();
new ForkJoinMergeSort().sort(rawArr2);
//System.out.println(Arrays.toString(new ForkJoinMergeSort().sort(rawArr2)));
endtime = new Date().getTime();
System.out.println("Fork/Join归并排序花费时间:" + (endtime - begintime));
System.out.println("是否升序:"+CommonUtil.isSorted(rawArr2, true));
} /**
* 生成int类型的数组
*
* @return
*/
private static int[] generateIntArray(int length) {
int[] intArr = new int[length];
for (int i = 0; i < length; i++) {
intArr[i] = new Double(Math.random() * length).intValue();
}
return intArr;
}
}
 

以下是数组容量在各个量级时,两种方法效率对比:

数组长度 100 1000 10000 100000 1000000 10000000
单线程 (ms) 1 2 7 33 188 2139
Fork/Join (ms) 8 9 17 63 358 1133

通过统计可以发现,当待排序序列长度较小时,使用单线程效率要高于多线程,但是随着数量不断增加,多线程执行时间越来越接近单线程的执行时间,最终在1000万这个量级开始速率远超单线程。工作中不能滥用多线程,在该使用的时候使用可以加快效率,充分利用多核。但是在不该用的时候使用徒增工作量,有可能效率还不如单线程。 感兴趣的朋友可以通过下面代码地址找到运行的全部源码自己跑跑试试看。

五、本文代码地址

包括本篇在内以后所有代码统一存放地址为: 
https://github.com/mingbozhang/algorithm

六、参考

https://docs.oracle.com/javase/tutorial/essential/concurrency/forkjoin.html 
《算法设计与分析基础(第3版)》

java归并排序,单线程vs多线程的更多相关文章

  1. Java IO学习笔记七:多路复用从单线程到多线程

    作者:Grey 原文地址:Java IO学习笔记七:多路复用从单线程到多线程 在前面提到的多路复用的服务端代码中, 我们在处理读数据的同时,也处理了写事件: public void readHandl ...

  2. java 并发性和多线程 -- 读感 (一 线程的基本概念部分)

    1.目录略览      线程的基本概念:介绍线程的优点,代价,并发编程的模型.如何创建运行java 线程.      线程间通讯的机制:竞态条件与临界区,线程安全和共享资源与不可变性.java内存模型 ...

  3. Java 并发性和多线程

    一.介绍 在过去单 CPU 时代,单任务在一个时间点只能执行单一程序.之后发展到多任务阶段,计算机能在同一时间点并行执行多任务或多进程.虽然并不是真正意义上的“同一时间点”,而是多个任务或进程共享一个 ...

  4. Java 并发和多线程(一) Java并发性和多线程介绍[转]

    作者:Jakob Jenkov 译者:Simon-SZ  校对:方腾飞 http://tutorials.jenkov.com/java-concurrency/index.html 在过去单CPU时 ...

  5. zookeeper的c API 单线程与多线程问题 cli_st和cli_mt

    同样的程序,在centos和ubuntu上都没有问题,在solaris上问题却多多,据说是solaris管理更加严格. zookeeper_init方法,在传入一个错误的host也能初始化出一个非空的 ...

  6. Java简明教程 12.多线程(multithreading)

    单线程和多线程 关于它们的区别,zhihu上有一个回答,我认为十分不错,如下: . 单进程单线程:一个人在一个桌子上吃菜. . 单进程多线程:多个人在同一个桌子上一起吃菜. . 多进程单线程:多个人每 ...

  7. 1、Java并发性和多线程-并发性和多线程介绍

    以下内容转自http://ifeve.com/java-concurrency-thread/: 在过去单CPU时代,单任务在一个时间点只能执行单一程序.之后发展到多任务阶段,计算机能在同一时间点并行 ...

  8. Spring Boot 定时任务单线程和多线程

    Spring Boot 的定时任务: 第一种:把参数配置到.properties文件中: 代码: package com.accord.task; import java.text.SimpleDat ...

  9. Java基础】并发 - 多线程

    Java基础]并发 - 多线程 分类: Java2014-05-03 23:56 275人阅读 评论(0) 收藏 举报 Java   目录(?)[+]   介绍 Java多线程 多线程任务执行 大多数 ...

随机推荐

  1. mybatis里的foreach语句

    相信用了Mybatis的朋友们,都曾有一个疑惑,就是foreach是怎么用的,下面我就简单讲讲我的理解: foreach主要用在SQL语句中迭代一个集合.foreach元素的属性主要由item,ind ...

  2. android案例一 电话拨号器

    效果图:   电话拨号器的核心原理:  意图   MainActivity代码:     private EditTest et_number; //加载一个布局 setContentView(R.l ...

  3. c#学习 流程控制语句

    语句是啥? 语句就是程序的基本结构.程序是一个人,语句就是人的身体.而写程序的人就是上帝造人的过程. break在swich语句中很严谨 using System; public class Grad ...

  4. String 与StringBuilder有什么区别

    C# String 对象是不可改变的.每次使用 System.String 类中的方法之一时,都要在内存中创建一个新的字符串对象,这就需要为该新对象分配新的空间.在需要对字符串执行重复修改的情况下,与 ...

  5. c# 如何隐藏TabControl控件的标签

    http://www.cnblogs.com/chenleiustc/archive/2009/11/25/1527813.html 方法一:将标签缩小到机会看不到:设置页面的大小模式会自动适合(会尽 ...

  6. 一周试用yii开发一个带各种该有功能的web程序(三)

    接上篇,为什么index 模版里没有任何有关require,inculde类型关键字,却任然有我们认为“多余”的字符出现.因为在至少我的认知里html的结构该是<html><head ...

  7. PowerShell 连接SQL

    因为对SQL操作比较多,但有些操作其实都是重复性的,只是参数不太一样了,例如silo id, server name 等.希望可以通过powershell脚本提高效率. 尝试如下 1. 使用Power ...

  8. redmine export long csv file failed: 502 proxy error

    After modified the file \apps\redmine\conf\httpd-vhosts.conf: <VirtualHost *:8080> ServerName ...

  9. nginx auth

    location / { auth_basic "closed site";##Context:http, server, location, limit_except auth_ ...

  10. python:点赞功能

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/ ...