本文章由cartzhang编写。转载请注明出处。 所有权利保留。

文章链接:http://blog.csdn.net/cartzhang/article/details/55051570

作者:cartzhang

simple pool 博客一的地址:

http://blog.csdn.net/cartzhang/article/details/54096845

一、simpe_pool对象池的问题

在上篇对象池simple_pool中提到了它如今的问题。

一个是数据控制,也就是在单个父亲节点下,仅仅会一直增加来满足当前游戏对对象池内的对象数量的须要,没有考虑到降低。也就是说,若在A阶段,游戏场景中同一时候须要大量的某个池内O1对象,这时候就出现大量的AO1对象在内存中。可是过了A阶段,不须要A这么多O1对象时候,对象池内没有做操作和优化。

还有一个问题,就是多线程的问题。这个暂时不这里讨论。

有须要能够自己先对linkedlist加锁。



本片针对第一个问题。来做了处理。

顺便给出上篇simple_pool博客地址:

http://blog.csdn.net/cartzhang/article/details/54096845

https://github.com/cartzhang/simple_pool_bench

二、对象池内数量优化思路

本着在后台处理,尽量少影响对象池的使用的原则。决定使用在增加一个线程来实现。对须要清理的池内对象进行记录和推断。然后在主线程中进行删除(destroy)操作.



由于Unity不同意在其它自己建立的线程中调用Destroy函数,所以还是要在Update中进行处理。

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY2FydHpoYW5n/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="这里写图片描写叙述" title="">



这个是自己画的流程图,基本就是这个样子。当然里面详细參数,能够依据自己须要调整。

三、实现代码

1.首先,建立垃圾清理标记和垃圾对象列表。

// garbage clean.
private static bool clearGarbageFlag = false;
private static List<Transform> GarbageList = new List<Transform>(100);

然后标记为真。開始清理。

这里进行了加锁,避免一边删除。一边增加,这样造成野的对象。垃圾表被清空了,对象也不在对象池内列表中。

2.主线程的清理工作

 private void Update()
{
CleanGarbageList();
} private void CleanGarbageList()
{
if (clearGarbageFlag)
{
clearGarbageFlag = false;
lock (GarbageList)
{
Debug.Assert(GarbageList.Count > 0);
Debug.Log("now destroy " + GarbageList.Count + " from pool" + GarbageList[0].name);
for (int i = 0; i < GarbageList.Count; i++)
{
Destroy(GarbageList[i].gameObject);
}
GarbageList.Clear();
}
}
}

3.优化线程启动



线程在第一次调用池的时候開始启动。

然后定时检測数量,每轮会检測checkTimesForEach个对象池内的未使用的情况。对象池个数超出的部分,等待5个中的一个多次检測没有异常后,在增加到检測中,这个主要是为了防止多个对象池内,一次性增加垃圾列表中太多对象。须要一次性删掉的太多,造成主线程卡顿的情况。



当然这也不是最理想的,为了防止卡顿。每次检測循环仅仅检測到一个满足垃圾清理条件,须要处理就会停止检測跳出循环,然后进行垃圾处理。这个也是为了轻量级的删减措施。

一旦成功设置标志。就又一次计算和检測。而且在设置后,优化线程等待1秒时间,来让主线程做工作。这个时间应该是非常充裕的。

主要代码

 private static void OptimizationPool()
{
// check cpu time to start
Thread.Sleep(100);
// 检測间隔时间20秒
float intervalTimeTodetect = 20f;
// after how many times counts to reset count.
// 循环检測多少次。后记录清零。 每次仅仅处理须要处理的前5个池。
const int checkTimesForEach = 5;
// 暂时池管理对象
Dictionary<int, ObjectPool> poolManagerTempDic = new Dictionary<int, ObjectPool>();
System.DateTime timeCount = System.DateTime.Now;
// 间隔时间内运行一次
bool eachMinuteGetDicOnce = false;
// 每一个池未使用对象超过一半的标记,记录次数
Dictionary<int, int> CurrentPoolUnuseCount = new Dictionary<int, int>();
// 检測刷新次数。也是一次计数的最大时间。
int icountLoopTime = 0;
Debug.Log("Thread start");
// 休眠时间
int sleepTime = 10;
while (isStartThread)
{
Thread.Sleep(sleepTime);
if (!eachMinuteGetDicOnce)
{
eachMinuteGetDicOnce = true;
poolManagerTempDic = poolManagerDic.ToDictionary(entry => entry.Key,entry => entry.Value);
// loop check 3 time to reset.
if (icountLoopTime % checkTimesForEach == 0)
{
CurrentPoolUnuseCount.Clear();
icountLoopTime = icountLoopTime > 10000000 ? 0 : icountLoopTime;
} // mark unuse nuber for all.
foreach(var element in poolManagerTempDic)
{
ObjectPool opool = element.Value;
int unusinglinkCount = opool.UnusingLinkedList.Count;
// half of all is useless and more than 10.
if (unusinglinkCount * 2 > unusinglinkCount + opool.UsingLinkedList.Count && unusinglinkCount > 10)
{
MarkCountForUnusingLink(ref CurrentPoolUnuseCount, element.Key);
// satisfy the condition,add unusing link gameobject to garbagelist.
int currentMark = 0;
CurrentPoolUnuseCount.TryGetValue(element.Key,out currentMark);
// be marked three times, add to garbage list.
if (currentMark >= 3)
{
AddObjectsToGarbageList(ref opool.UnusingLinkedList);
// count tick to reset.
CurrentPoolUnuseCount[element.Key] = 0;
clearGarbageFlag = true;
// each time only gathing one pool to process.
break;
}
}
}
}
// leave time for mainthread to delete gameobjects.
if (clearGarbageFlag)
{
icountLoopTime = 0;
intervalTimeTodetect = 20f;
Thread.Sleep(1000);
timeCount = System.DateTime.Now;
} // interval 20 seconds to start check once;
if ((System.DateTime.Now - timeCount).TotalSeconds > intervalTimeTodetect)
{
timeCount = System.DateTime.Now;
eachMinuteGetDicOnce = false;
poolManagerTempDic.Clear();
icountLoopTime++;
Debug.Log("Loop count is " + icountLoopTime);
}
// long time nothing happen, expand the detective interval time (max <= 90s).
if (icountLoopTime >= 4 )
{
intervalTimeTodetect = intervalTimeTodetect * 2 >= 90f ? 90f : intervalTimeTodetect * 2;
icountLoopTime = 0;
Debug.Log("interval time is " + intervalTimeTodetect);
}
}
return;
} /// <summary>
/// add last gameobject to garbagelist,as when unsing unusinglink is from first place to get.
/// </summary>
/// <param name="list"></param>
private static void AddObjectsToGarbageList(ref LinkedList<Transform> list)
{
Debug.Assert(list.Count > 0);
int FlagDestroyNumber = list.Count>>1;
for (int i = 0; i < FlagDestroyNumber; i++)
{
GarbageList.Add(list.Last.Value);
list.RemoveLast();
}
}

依照眼下设置參数,開始检測的时间间隔为20秒,若20*2=40秒后,没有须要处理的垃圾,就把检測时间间隔翻倍为40秒检測一次;若在过40*4=160秒,没有触发标志。检測时间进一步延长,逐次翻倍增加。可是最大值为90秒。

也就是说。最大的检測间隔为90秒。



若中间被打断。所有归为正常20秒检測一次。

触发增加垃圾列表的条件:



设置触发标志是一个池,在3次检測中都有超过一半的对象没有被使用,而且总体未使用数量超过10个。

四、更新project分享地址:

地址:https://github.com/cartzhang/simple_pool_bench



能够下载Assets文件。然后用unity測试。



这个測试demo为 PoolTimeOptimizeOjbectsDemo.unity。



优化流程图下载地址:

https://github.com/cartzhang/simple_pool_bench/blob/master/img/pool_bench_optimize.png



若有问题,请提交问题或代码。非常感谢!

。!

五、附件

poolManager.cs 所有代码:

using UnityEngine;
using System.Collections.Generic;
using SLQJ_POOL;
using UnityEngine.Internal;
using System.Threading;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Linq; /// <summary>
/// 扩展GameObject函数
/// 使用扩展函数方面代码调用,能够Unity的随意代码中,调用对象池来产生和回池对象,
/// 而且不须要调用命名空间等繁琐东西。 /// 代码样例: obj = gameObject.InstantiateFromPool(prefab);
/// gameObject.DestroyToPool(poolObjs[poolObjs.Count - 1], 0.2f);
/// 详细能够參考HowToUse脚本和TestEffect。 /// @cartzhang
/// </summary>
public static class GameObjectExten
{
/// <summary>
/// 调用对象池,产生一对象,并带有位置和旋转等參考
/// </summary>
/// <param name="gameobject"></param>
/// <param name="original"></param>
/// <param name="position"></param>
/// <param name="rotation"></param>
/// <returns></returns>
public static GameObject InstantiateFromPool(this GameObject gameobject, Object original, Vector3 position, Quaternion rotation)
{
return PoolManager.PullObjcetFromPool(original as GameObject, position, rotation).gameObject;
}
/// <summary>
/// 调用对象池,产生对象
/// </summary>
/// <param name="gameobject"></param>
/// <param name="original"></param>
/// <returns></returns>
public static GameObject InstantiateFromPool(this GameObject gameobject, Object original)
{
return PoolManager.PullObjcetFromPool(original as GameObject).gameObject;
}
/// <summary>
/// 对象返回对象池
/// </summary>
/// <param name="gameobject"></param>
/// <param name="obj"></param>
public static void DestroyToPool(this GameObject gameobject, Object obj)
{
PoolManager.PushObjectPool((obj as GameObject).transform);
}
/// <summary>
/// 带延时的返回对象池
/// </summary>
/// <param name="gameobject"></param>
/// <param name="obj"></param>
/// <param name="t"></param>
public static void DestroyToPool(this GameObject gameobject, Object obj, [DefaultValue("0.0F")] float t)
{
PoolManager.PushObjectPool((obj as GameObject).transform, t);
}
} namespace SLQJ_POOL
{
public class PoolManager : MonoBehaviour
{
private static PoolManager instance;
private static bool bStartThreadOnce = false;
private static bool isStartThread = false;
private static Thread tOptimizationThread; private static List<GameObject> prefabList = new List<GameObject>();
//存放预制体相应的id。ObjcetPool
public static Dictionary<int, ObjectPool> poolManagerDic = new Dictionary<int, ObjectPool>();
private static Dictionary<Transform, ObjectPool> transformDic = new Dictionary<Transform, ObjectPool>();
// garbage clean.
private static bool clearGarbageFlag = false;
private static List<Transform> GarbageList = new List<Transform>(100);
private void Update()
{
CleanGarbageList();
} private void CleanGarbageList()
{
if (clearGarbageFlag)
{
clearGarbageFlag = false;
lock (GarbageList)
{
Debug.Assert(GarbageList.Count > 0);
Debug.Log("now destroy " + GarbageList.Count + " from pool" + GarbageList[0].name);
for (int i = 0; i < GarbageList.Count; i++)
{
Destroy(GarbageList[i].gameObject);
}
GarbageList.Clear();
}
}
} //初始化某个预制体相应的对象池
public static void InitPrefab(GameObject prefab, int initNum = 4)
{
GetObjectPool(prefab, initNum);
}
//外界调用的接口
public static Transform PullObjcetFromPool(GameObject prefab)
{
return _PullObjcetFromPool(prefab);
}
public static Transform PullObjcetFromPool(GameObject prefab, Vector3 pos, Quaternion quaternion)
{
return _PullObjcetFromPool(prefab, pos, quaternion);
}
private static Transform _PullObjcetFromPool(GameObject prefab)
{
if (prefab == null)
{
Debug.Log("prefab is null!");
return null;
}
ObjectPool objPool = GetObjectPool(prefab);
StartThreadOnce();
return objPool.PullObjcetFromPool();
} private static Transform _PullObjcetFromPool(GameObject prefab, Vector3 pos, Quaternion quaternion)
{
if (prefab == null)
{
Debug.Log("prefab is null!");
return null;
}
ObjectPool objPool = GetObjectPool(prefab, pos, quaternion);
StartThreadOnce();
return objPool.PullObjcetFromPool(pos, quaternion);
} private static ObjectPool GetObjectPool(GameObject prefab, int initNum = 4)
{
ObjectPool objPool = null;
//推断集合中是否有预制体相应的对象池
int leng = prefabList.Count;
int prefabID = prefab.GetInstanceID();
for (int i = 0; i < leng; i++)
{
if (prefabID == prefabList[i].GetInstanceID())
{
objPool = poolManagerDic[prefabID];
break;
}
}
//没有找到对象池的话创建一个对象池
if (objPool == null)
{
objPool = CreatObjcetPool(prefab, initNum);
}
return objPool;
} private static ObjectPool GetObjectPool(GameObject prefab, Vector3 pos, Quaternion qua, int initNum = 4)
{
ObjectPool objPool = null;
int leng = prefabList.Count;
int prefabID = prefab.GetInstanceID();
for (int i = 0; i < leng; i++)
{
if (prefabID == prefabList[i].GetInstanceID())
{
objPool = poolManagerDic[prefabID];
}
}
if (objPool == null)
{
objPool = CreatObjcetPool(prefab, pos, qua, initNum);
}
return objPool;
} private static ObjectPool CreatObjcetPool(GameObject prefab, Vector3 pos, Quaternion qua, int initNum)
{
prefabList.Add(prefab);
GameObject go = new GameObject();
go.name = prefab.name + "Pool";
ObjectPool objPool = go.AddComponent<ObjectPool>();
objPool.InitObjectPool(prefab, pos, qua, transformDic, initNum);
poolManagerDic.Add(prefab.GetInstanceID(), objPool);
return objPool;
} private static ObjectPool CreatObjcetPool(GameObject prefab, int initNum)
{
prefabList.Add(prefab);
GameObject go = new GameObject();
go.name = prefab.name + "Pool";
ObjectPool objPool = go.AddComponent<ObjectPool>();
objPool.InitObjectPool(prefab, transformDic, initNum);
poolManagerDic.Add(prefab.GetInstanceID(), objPool);
return objPool;
} public static void PushObjectPool(Transform handleTransform)
{
ObjectPool objPool = GetPoolByTransform(handleTransform);
if (objPool)
{
objPool.PushObjectToPool(handleTransform);
}
else
{
GameObject.Destroy(handleTransform.gameObject);
}
}
public static void PushObjectPool(Transform handleTransform, float delayTime)
{
ObjectPool objPool = GetPoolByTransform(handleTransform);
if (objPool)
{
objPool.PushObjectToPool(handleTransform, delayTime);
}
else
{
GameObject.Destroy(handleTransform.gameObject, delayTime);
}
}
//马上回池的接口
public static void PushObjectPool(Transform handleTransform, GameObject prefab)
{
ObjectPool objPool = GetObjectPool(prefab);
objPool.PushObjectToPool(handleTransform.transform);
}
//延迟回池的接口
public static void PushObjectPool(Transform handleTransform, GameObject prefab, float delayTime)
{
ObjectPool objPool = GetObjectPool(prefab);
objPool.PushObjectToPool(handleTransform, delayTime);
} private static ObjectPool GetPoolByTransform(Transform handleTransform)
{
if (transformDic.ContainsKey(handleTransform))
{
return transformDic[handleTransform];
}
Debug.LogError(handleTransform.name + " no find it's ObjectPool");
return null;
}
// add code to clean pool from time to time.
private static void StartThreadOnce()
{
// start thread to clean pool from time to time.
if (!bStartThreadOnce)
{
bStartThreadOnce = true;
ThreadPool.QueueUserWorkItem(AutoToCheckOptimization);
}
} private static void AutoToCheckOptimization(object obj)
{
Thread.Sleep(10);
isStartThread = true;
tOptimizationThread = new Thread(OptimizationPool);
tOptimizationThread.Start();
} private static void OptimizationPool()
{
// check cpu time to start
Thread.Sleep(100);
// 检測间隔时间20秒
float intervalTimeTodetect = 20f;
// after how many times counts to reset count.
// 循环检測多少次,后记录清零。每次仅仅处理须要处理的前5个池。
const int checkTimesForEach = 5;
// 暂时池管理对象
Dictionary<int, ObjectPool> poolManagerTempDic = new Dictionary<int, ObjectPool>();
System.DateTime timeCount = System.DateTime.Now;
// 间隔时间内运行一次
bool eachMinuteGetDicOnce = false;
// 每一个池未使用对象超过一半的标记,记录次数
Dictionary<int, int> CurrentPoolUnuseCount = new Dictionary<int, int>();
// 检測刷新次数。也是一次计数的最大时间。 int icountLoopTime = 0;
Debug.Log("Thread start");
// 休眠时间
int sleepTime = 10;
while (isStartThread)
{
Thread.Sleep(sleepTime);
if (!eachMinuteGetDicOnce)
{
eachMinuteGetDicOnce = true;
poolManagerTempDic = poolManagerDic.ToDictionary(entry => entry.Key,entry => entry.Value);
// loop check 3 time to reset.
if (icountLoopTime % checkTimesForEach == 0)
{
CurrentPoolUnuseCount.Clear();
icountLoopTime = icountLoopTime > 10000000 ? 0 : icountLoopTime;
} // mark unuse nuber for all.
foreach(var element in poolManagerTempDic)
{
ObjectPool opool = element.Value;
int unusinglinkCount = opool.UnusingLinkedList.Count;
// half of all is useless and more than 10.
if (unusinglinkCount * 2 > unusinglinkCount + opool.UsingLinkedList.Count && unusinglinkCount > 10)
{
MarkCountForUnusingLink(ref CurrentPoolUnuseCount, element.Key);
// satisfy the condition,add unusing link gameobject to garbagelist.
int currentMark = 0;
CurrentPoolUnuseCount.TryGetValue(element.Key,out currentMark);
// be marked three times, add to garbage list.
if (currentMark >= 3)
{
AddObjectsToGarbageList(ref opool.UnusingLinkedList);
// count tick to reset.
CurrentPoolUnuseCount[element.Key] = 0;
clearGarbageFlag = true;
// each time only gathing one pool to process.
break;
}
}
}
}
// leave time for mainthread to delete gameobjects.
if (clearGarbageFlag)
{
icountLoopTime = 0;
intervalTimeTodetect = 20f;
Thread.Sleep(1000);
timeCount = System.DateTime.Now;
} // interval 20 seconds to start check once;
if ((System.DateTime.Now - timeCount).TotalSeconds > intervalTimeTodetect)
{
timeCount = System.DateTime.Now;
eachMinuteGetDicOnce = false;
poolManagerTempDic.Clear();
icountLoopTime++;
Debug.Log("Loop count is " + icountLoopTime);
}
// long time nothing happen, expand the detective interval time (max <= 90s).
if (icountLoopTime >= 4 )
{
intervalTimeTodetect = intervalTimeTodetect * 2 >= 90f ? 90f : intervalTimeTodetect * 2;
icountLoopTime = 0;
Debug.Log("interval time is " + intervalTimeTodetect);
}
}
return;
} private static void MarkCountForUnusingLink(ref Dictionary<int, int> poolUnuseCount,int prefabGuid)
{
Debug.Assert(null != poolManagerDic);
int currentMark = 0;
if (poolUnuseCount.ContainsKey(prefabGuid))
{
poolUnuseCount.TryGetValue(prefabGuid, out currentMark);
}
currentMark++;
if (poolUnuseCount.ContainsKey(prefabGuid))
{
poolUnuseCount[prefabGuid] = currentMark;
}
else
{
poolUnuseCount.Add(prefabGuid, currentMark);
}
} /// <summary>
/// add last gameobject to garbagelist,as when unsing unusinglink is from first place to get.
/// </summary>
/// <param name="list"></param>
private static void AddObjectsToGarbageList(ref LinkedList<Transform> list)
{
Debug.Assert(list.Count > 0);
int FlagDestroyNumber = list.Count>>1;
for (int i = 0; i < FlagDestroyNumber; i++)
{
GarbageList.Add(list.Last.Value);
list.RemoveLast();
}
} public void Dispose()
{
isStartThread = false;
}
}
}

simple_pool对象池——优化&lt;二&gt;的更多相关文章

  1. Unity 游戏框架搭建 (二十) 更安全的对象池

    上篇文章介绍了,只需通过实现IObjectFactory接口和继承Pool类,就可以很方便地实现一个SimpleObjectPool.SimpleObjectPool可以满足大部分的对象池的需求.而笔 ...

  2. 二十、dbms_stats(用于搜集,查看,修改数据库对象的优化统计信息)

    1.概述 作用:用于搜集,查看,修改数据库对象的优化统计信息. 2.包的组成 1).get_column_stats作用:用于取得列的统计信息语法:dbms_stats.get_column_stat ...

  3. Unity性能优化-对象池

    1.对象池Object Pool的原理: 有些GameObject是在游戏中需要频繁生成并销毁的(比如射击游戏中的子弹),以前的常规做法是:Instantiate不断生成预设件Prefab,然后采用碰 ...

  4. Mysql线程池优化笔记

    Mysql线程池优化我是总结了一个站长的3篇文章了,这里我整理到一起来本文章就分为三个优化段了,下面一起来看看.     Mysql线程池系列一(Thread pool FAQ) 首先介绍什么是mys ...

  5. Unity 对象池的使用

    在游戏开发过程中,我们经常会遇到游戏发布后,测试时玩着玩着明显的感觉到有卡顿现象.出现这种现象的有两个原因:一是游戏优化的不够好或者游戏逻辑本身设计的就有问题,二是手机硬件不行.好吧,对于作为程序员的 ...

  6. Unity 游戏框架搭建 (十九) 简易对象池

    在Unity中我们经常会用到对象池,使用对象池无非就是解决两个问题: 一是减少new时候寻址造成的消耗,该消耗的原因是内存碎片. 二是减少Object.Instantiate时内部进行序列化和反序列化 ...

  7. Java小对象的解决之道——对象池(Object Pool)的设计与应用

    一.概述 面向对象编程是软件开发中的一项利器,现已经成为大多数编程人员的编程思路.很多高级计算机语言也对这种编程模式提供了很好的支持,例如C++.Object Pascal.Java等.曾经有大量的软 ...

  8. 大数据技术之_27_电商平台数据分析项目_02_预备知识 + Scala + Spark Core + Spark SQL + Spark Streaming + Java 对象池

    第0章 预备知识0.1 Scala0.1.1 Scala 操作符0.1.2 拉链操作0.2 Spark Core0.2.1 Spark RDD 持久化0.2.2 Spark 共享变量0.3 Spark ...

  9. netty源码分析 - Recycler 对象池的设计

    目录 一.为什么需要对象池 二.使用姿势 2.1 同线程创建回收对象 2.2 异线程创建回收对象 三.数据结构 3.1 物理数据结构图 3.2 逻辑数据结构图(重要) 四.源码分析 4.2.同线程获取 ...

随机推荐

  1. Number of Connected Components in an Undirected Graph -- LeetCode

    Given n nodes labeled from 0 to n - 1 and a list of undirected edges (each edge is a pair of nodes), ...

  2. java代码中执行liunx命令

    public static String runShell(String command){ try{ String[] commands = isLinux()?new String[]{" ...

  3. .net / java /安卓des加密互通

    一 . C#.net /// <summary> /// 加密数据 /// </summary> /// <param name="Text"> ...

  4. Has anybody found a way to load HTTPS pages with an invalid server certificate using UIWebView?

    If a user attempts to load a https web page in Mobile Safari and the server's certificate validation ...

  5. Signing Identities, Missing Private Key, Cannot sign App

    这个问题发生在重新安装系统后,丢失了之前的private key等.所以解决方法就是提示的revoke and request. 到developer center中找到certificate中对应的 ...

  6. Android ToolBar 使用完全解析

    ToolBar简介 ToolBar是Android 5.0推出的一个新的导航控件用于取代之前的ActionBar,由于其高度的可定制性.灵活性.具有Material Design风格等优点,越来越多的 ...

  7. linux文件传输

    Linux下几种文件传输命令 sz rz sftp scp 最近在部署系统时接触了一些文件传输命令,分别做一下简单记录: 1.sftp常用来在两台linux之间传输文件 Secure Ftp 是一个基 ...

  8. log4j教程 9、HTMLLayout

    如果想生成一个HTML格式的文件,日志信息,那么可以使用 org.apache.log4j.HTMLLayout 格式化日志信息. HTMLLayout类扩展抽象org.apache.log4j.La ...

  9. ElasticSearch文档

    1.什么是文档? 程序中大多的实体或对象能够被序列化为包含键值对的JSON对象,键(key)是字段(field)或属性(property)的名字,值(value)可以是字符串.数字.布尔类型.另一个对 ...

  10. 通过java api提交自定义hadoop 作业

    通过API操作之前要先了解几个基本知识 一.hadoop的基本数据类型和java的基本数据类型是不一样的,但是都存在对应的关系 如下图 如果需要定义自己的数据类型,则必须实现Writable hado ...