并发遍历二叉树 Java 实现
2013-04-16 11:11
441 查看
单线程遍历二叉树是数据结构书中必讲的算法, 但多线程访问呢?
我最近写了一个多线程遍历二叉树的算法, 当然既然是多线程,就不保证一定的顺序, 只要节点能访问到就算数.
2) 当线程池有空余线程把树的右子树拆分给另外一个线程处理, 否则自己处理右子数
工作线程过一定时间间隔就会检查线程池是否有空闲线程, 有则分离当前节点的右子树并委托给ThreadPool
2)判断所有工作都结束
启动工作任务时用 HashSet 记录, 工作任务结束时清楚HashSet 记录, 当HashSet size 为0 时,所有工作结束.
2) 主线程结束, 程序并不会结束, 想让主线程等待 ThreadPool 结束, 要调用ThreadPool 的 awaitTermination 方法
程序
在程序中,我调用 Thread.sleep() 模拟处理时间, 否则启动线程池, 线程调度的开销将大于节点访问时间, 体现不出多线程的优势.
在i3上跑得的输出: 似乎效率提高了1倍
有时间把此程序放到4核的机器上跑, 看究竟效率能提高多少?
我最近写了一个多线程遍历二叉树的算法, 当然既然是多线程,就不保证一定的顺序, 只要节点能访问到就算数.
算法的基本思路
1) 使用 java.util.concurrent.ThreadPoolExecutor 构造线程池2) 当线程池有空余线程把树的右子树拆分给另外一个线程处理, 否则自己处理右子数
要考虑的情况
1) 如果二叉树非常不平衡(左子树很深,右子树很浅), 会出现一个线程忙碌,而另外一个线程早结束的情况工作线程过一定时间间隔就会检查线程池是否有空闲线程, 有则分离当前节点的右子树并委托给ThreadPool
2)判断所有工作都结束
启动工作任务时用 HashSet 记录, 工作任务结束时清楚HashSet 记录, 当HashSet size 为0 时,所有工作结束.
注意事项
1) 即使任务结束,ThreadPool 还是在运行的( waiting task ), 要调用 shutdown 通知 ThreadPool 结束2) 主线程结束, 程序并不会结束, 想让主线程等待 ThreadPool 结束, 要调用ThreadPool 的 awaitTermination 方法
程序
package com.pnp.javathreadpool; import java.util.HashSet; import java.util.Random; import java.util.Stack; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class ConcurTravBTree { class BTreeNode<T extends Comparable> { public BTreeNode<T> left; public BTreeNode<T> right; public T value; public BTreeNode(BTreeNode<T> left, BTreeNode<T> right, T value) { this.left = left; this.right = right; this.value = value; } public BTreeNode(T value) { this(null, null, value); } } public class BTree<T extends Comparable> { String dumpStr; public BTreeNode<T> root; public BTree() { } public BTree(BTreeNode<T> root) { this.root = root; } public void orderAdd(T v) { if (root == null) root = new BTreeNode(v); else orderAdd(root, v); } public void orderAdd(BTreeNode<T> node, T v) { if (node.value.compareTo(v) > 0) { if (node.left == null) node.left = new BTreeNode(v); else orderAdd(node.left, v); } else { if (node.right == null) node.right = new BTreeNode(v); else orderAdd(node.right, v); } } public void preOrderTraverse() { preOrderTraverse(root); } public void preOrderTraverse( BTreeNode<T> node) { if (node != null) { //System.out.println(node.value); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } preOrderTraverse(node.left); preOrderTraverse(node.right); } } } static int global_id = 0; public static final int POOLSIZE = 4; ThreadPoolExecutor threadPool; BTree<Integer> tree; HashSet<Integer> tasks = new HashSet<Integer>(); public void init() { tree = new BTree<Integer>(); /* int[] data = new int[] { 1, 3, 5 }; for (int i = 0; i < data.length; i++) tree.orderAdd(data[i]); */ Random r = new Random(); for( int i=0; i< 500; i++) { tree.orderAdd( r.nextInt(500)); } threadPool = new ThreadPoolExecutor(POOLSIZE, // corePoolSize POOLSIZE + 2, // maximumPoolSize 60, // keepAliveTime TimeUnit.SECONDS, // keepAliveTime unit new ArrayBlockingQueue<Runnable>(POOLSIZE + 2), // workQueue new ThreadPoolExecutor.CallerRunsPolicy()); // handler } public void start() { threadPool.execute(new Visitor(tree.root)); } class Visitor implements Runnable { private int id; private BTreeNode<Integer> node; public Visitor(BTreeNode<Integer> node) { this.id = ++global_id; this.node = node; } public boolean handleNode(BTreeNode<Integer> node) { if (node == null) return true; if (ConcurTravBTree.this.threadPool.getActiveCount() <= ConcurTravBTree.this.threadPool .getCorePoolSize()) { System.out.println("--- ActiveCount " + ConcurTravBTree.this.threadPool.getActiveCount()); ConcurTravBTree.this.threadPool.execute(new Visitor(node)); return true; } return false; } public void run() { System.out.println("[ job" + id + " ] ---start "); tasks.add(id); if (handleNode(node.right)) // assign Node to a task node.right = null; // // pre-order traverse Stack<BTreeNode<Integer>> stack = new Stack<BTreeNode<Integer>>(); while (node != null || !stack.isEmpty()) { long start = System.currentTimeMillis(); while (node != null) { //System.out.println("[ job" + id + " ]" + node.value); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } stack.push(node); node = node.left; } node = stack.pop(); node = node.right; long cur = System.currentTimeMillis(); if (cur - start > 1000) { if (handleNode(node)) { node = null; } } } System.out.println("[ job" + id + " ] ---end"); tasks.remove(id); if ( tasks.size() == 0) { threadPool.shutdown(); } } } public static void main(String[] args) { ConcurTravBTree tt = new ConcurTravBTree(); System.out.println("init tree..."); tt.init(); System.out.println("start preOrderTraverse traverse..."); long start = System.currentTimeMillis(); tt.tree.preOrderTraverse(); long end = System.currentTimeMillis(); System.out.println("---- preOrderTraverse cost " + (end - start)); System.out.println("start concurrent traverse..."); long cstart = System.currentTimeMillis(); tt.start(); try { tt.threadPool.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS); } catch (InterruptedException e) { e.printStackTrace(); } long cend = System.currentTimeMillis(); System.out.println("---- current traverse cost " + (cend - cstart)); } }
在程序中,我调用 Thread.sleep() 模拟处理时间, 否则启动线程池, 线程调度的开销将大于节点访问时间, 体现不出多线程的优势.
在i3上跑得的输出: 似乎效率提高了1倍
init tree... start preOrderTraverse traverse... ---- preOrderTraverse cost 50023 start concurrent traverse... [ job1 ] ---start --- ActiveCount 1 [ job2 ] ---start --- ActiveCount 2 [ job3 ] ---start --- ActiveCount 3 [ job4 ] ---start --- ActiveCount 4 [ job3 ] ---end [ job5 ] ---start --- ActiveCount 4 [ job5 ] ---end [ job6 ] ---start --- ActiveCount 4 [ job4 ] ---end [ job7 ] ---start [ job6 ] ---end [ job7 ] ---end [ job1 ] ---end [ job2 ] ---end ---- current traverse cost 27116
有时间把此程序放到4核的机器上跑, 看究竟效率能提高多少?
相关文章推荐
- Java实现带行号层序遍历二叉树的变种问题(使用二维数组保存)
- 二叉树三种遍历递归及非递归实现(Java)
- 二叉树的遍历(Java实现)
- java语言实现的二叉树的各种操作(包括递归与非递归遍历二叉树,求二叉树的高度,节点总数,叶子节点等)
- 根据前序遍历和中序遍历重建二叉树的Java实现
- Java实现二叉树的前序、中序、后序遍历(非递归方法)
- 二叉树的逐层遍历--java实现
- 用java实现二叉树的前序、中序、后序、层次遍历(递归和非递归版)
- 二叉树的遍历2(java实现)
- Java实现二叉树及相关遍历方式
- 非递归遍历二叉树--java实现
- java语言实现二叉树的前序、中序与后序遍历(递归与非递归)
- 【LeetCode-面试算法经典-Java实现】【107-Binary Tree Level Order Traversal II(二叉树层序遍历II)】
- java实现二叉树的先序、中序、后序遍历
- Java实现二叉树的多种遍历
- java实现二叉树的构建和遍历
- java实现二叉树的构建以及3种遍历方法
- java 实现二叉树的构建,先序,中序,后序,层次,递归,非递归的遍历
- java实现二叉树的深度优先遍历和广度优先遍历
- Java方式实现二叉树的前中后序遍历的递归及非递归算法