china-pub购书网址:http://www.china-pub.com/computers/common/info.asp?id=34017

1、总体感受
a) 这本书主要介绍的是intel平台下的多核程序设计技术,Windows介绍较多,Linux介绍较少,Java更少。作者是Intel公司的平台架构师,我们知道wintel联盟,书中的内容如此分布也是正常。
b) 此书让我懂得了很多硬件方面的并行知识。
c) 此书介绍Windows API中和并发相关的部分,很详尽,比Jeffrey Richter的《Windows核心编成》有深度多了,也精辟多了。显然《多核程序设计》作者属于有经验的工程师,Jeffrey Richter只是一个写手,两者没有可比性。不过这方面的知识偶早已涉猎,当作复习一遍罢了。
d) 此书偏向底层,硬件和操作系统层面。更高层面的技术介绍较少。
e) 第一次了解OpenMP技术的一些细节。以前只听说,也没查过任何相关资料,在此书中看到了相关部分,挺有意思的,感觉那些语法很有趣。Herb Sutter也是要在语法方面动手。反正我在有了一个粗浅认识之后,觉得很有意思。

-------------------------

2、并发流程控制
Fence
在Java 中对应的是java.util.concurrent.CountDownLatch。最初接触CountDownLatch的时候,由于其实现很简单, 当时觉得是一个可有可无的工具类。但后来在不同的场景多次使用,发现很有用。在此书中再次发现类似的Fence,用于在共享存储多处理器或者多核环境中, 确保存储操作的一致性。我猜这属于业界并发流控制的典型手段了。


Barrier
在Java中对应的是java.util.concurrent.CyclicBarrier。在应用程序中,一个场景就是和定时器结合使用,countDown、await、reset,做定时定量批量处理。
我猜这也属于业界并发流程控制的典型手段了。

(CountDownLatch和CycliBarrier的实现代码都很简单,但很有用,他们都属于并发流程控制的典型手段)

-------------------------

3、非阻塞算法
InterLocked在Java中对应的是java.util.concurrent.atomic.xxx
书中提到了cache行乒乓球现象导致的性能问题,提高了非阻塞算法的复杂性问题。

关于性能问题,developerworks上有一片文章,有测试数据:
《Java 理论与实践: 流行的原子》 (http://www.ibm.com/developerworks/cn/java/j-jtp11234/index.html)
文章中的测试数据表明,直接使用atomic在1个和2个处理器时是最好的,4个处理器以上,使用java.util.concurrent.locks.ReentrantLock性能更好。

java.util.concurrent包,提供了很多高级的概念,隐藏了非阻塞算法带来的复杂度,其底层框架达到了最佳性能。

-------------------------

4、任务分解、数据分解以及数据流分解
此书中明确提出了这三个概念,很有用,让我在这方面的知识概念清晰化了。

任务分解
Java 中的Executor,提供了任务分解的手段和模式。任务分解提交给Executor执行。java.util.concurrent中提供了 Future,用于任务提交者和Executor之间的协调。Future是一种很好的手段,在很多涉及并发的库都提供。例如C++网络并发库中提供了 Future,Herb Sutter要在Visual C++中引入Future。

数据分解
数据分解的手段很多也很常见。 Java中,提供了一种高级的数据分解协同模式java.util.concurrent.Exchanger这个类。早在Java SE 5.0时,Exchanger只支持2Parties,Java SE 6.0支持n parties。偶想象过一些很酷的应用场景,写过模拟测试,但一直没有机会用于实际开发中。

数据流分解
书中提到了众多周知的producer/consumer问题。
其实java.util.concurrent.Exchanger类,既有数据分解,又有数据流分解,exchanger中的producer和consumer的角色会互换的,很有意思。

-------------------------

5、作为Java程序员的思考
Java SE 5.0之后,提供了util.concurrent包,功能齐全,性能卓越,非常优秀。从此书来看,业界流行的流程控制手段和并发程序设计方法一个不落。我们应该感谢伟大的Doug Lea,他为我们带了一个如此完美的并发库!
posted @ 2007-04-26 07:08 温少的日志 阅读(3271) | 评论 (0)编辑 收藏
 

一旦方案想清楚,剩余部分的工作效率瓶颈就在于你的手速了。最近一直看起点中文网上的《师士传说》,主角叶重一个强项就是手速。

最基本的就是盲打。不会盲打的通常属于“编码低能儿”。身边也有不会盲打的朋友,他们通常都有一个问题,就是眼高手低,说说还行,动手就不行。当然他们能够在IT研发领域还混得很好,是因为在其他方面拥有优秀的能力。

熟练掌握快捷键是关键。键盘和鼠标之间通常有较大的距离,手经常在键盘和鼠标之间移动,会降低效率,也容易导致疲劳,用鼠标过多,也容易导致龌龊的鼠标手。解决这个问题的办法,就是纯键盘操作,其实很多IDE的快捷键功能强大,足以让你纯键盘操作,高效率编码。

我比较熟悉的IDE是Eclipse,就以Eclispse来说吧。

Eclipse的keys列表中,属于Eclipse本身有180多个快捷键,要提高编码速度,就应该熟练使用其中绝大多数。

练习的办法:
1、在Windows/Preferences/General/keys中,使用Export,把快捷键导出,导出的格式是csv格式,Windows下可以用Excel直接打开,Linux下可以用OpenOffice打开,打开时选择分隔符为“,”。

2、挨个练习使用。每天练习一部分,反复练习,坚持一段时间。

3、开始的时候,把鼠标放到一个不方便使用的角落,尽量不要让自己用鼠标。

4、快捷键的组合使用需要加强训练。在不同场景下,认真考虑用怎样的组合快捷键最高效。

如此坚持一段时间之后,编码的过程会很流畅,速度就会大大提高。



posted @ 2007-04-25 07:41 温少的日志 阅读(3194) | 评论 (25)编辑 收藏
 

这是一个很老的问题了,经常在论坛上看到,很多人也写了相关的文章。我在这方面拥有较多的经验,我也谈一下我的看法吧。

我曾经实现过金蝶EAS BOS的多数据支持引擎,脚本引擎,也作过O-R Mapping的预研工作,曾经对多个O-R Mapping产品做过研究。

C++、Java、C#都源自Algol,这系列语言也称为Imperative语言,中文翻译为命令式语言。命令式语言建立在冯*诺依曼体系结构上,程序员必须处理变量管理、变量复制。这样的结果是增加了执行的效率,但带来了程序开发的艰苦。

LISP、Schema、Haskell等语言属于函数式语言,函数式语言基于数学函数,不使用变量或者赋值语句产生结果,使用函数的应用、条件表示和递归作为执行控制。函数式语言是更高级的程序设计语言,和命令式语言相比,编写更少的代码,更高的开发效率,这是函数式语言的明确有点。很多编程技术都首先应用于函数式语言,例如范型、垃圾收集等。很多函数式语言都增加了一些命令式语言的特征,增加效率和易用性。

SQL语言是一个领域专用语言,专门用于处理关系数据。他具备一些函数式语言的特征,在处理关系数据方面非常直观和简介。在处理选择、投影、聚合、排序等等操作方面,显然比在Java或者C#中要方便得多。SQL也是易学易用。很多非程序员都掌握SQL,在金蝶,大多需求人员都熟练掌握SQL。SQL的解释需要损耗一定的性能,在对性能极端要求的情况下,通常不使用关系数据库。例如Google Account采用Berkeley DB等。

现在关系数据库已经发展很成熟,数据库的一些技术发展得很好,包括事务等,其中一些从数据库中发展起来的技术,还应用到操作系统中了。在前些年面向对象技术狂热的时候,作了很多面向对象数据库的研究,但是都没有取得较大的成功。在主流的关系数据库中,大多都包括了面向对象的支持,例如Oracle、Sybase,都具备了很丰富的面向对象功能,但是很少任用。

现在有人跟我说起db4o这样的数据库,我认为db4o不会取得成功,因为他在错误的方向发展。

现在关系数据库最流行,最流行的应用开发语言包括Java、C#等,都是面向对象命令式语言。开发语言需要访问关系数据库,需要转换,转换的过程比较繁琐,于是就诞生了O-R Mapping技术。这是一种妥协的结果,面向对象技术在数据库领域无法取得成功,在面向对象开发语言中,就需要一种对象-关系映射技术。我相信,这种妥协产生的技术,会越来越流行。

我也认为,这是一个正确的选择。就如高级语言不会尝试取代汇编,无论高级语言有多好,最多在其上增加抽象层。例如Java使用bytecode、C#使用IL这样,使用一种抽象层,而不是尝试取代汇编。

O-R Mapping技术除了简单映射之外,需要一种OQL,混合SQL和面向对象特征,提供映射方便的同时,保留关系数据库提供的强大功能。例如聚合、排序等关系运算必须在OQL中提供。由于程序中的返回结果,有时不需要映射成对象,所以OQL必须提供另外一种功能,数据查询。很多O-R Mappping产品都提供了基于对象的OQL和基于数据的OQL。

这导致包括三个部分:
1、应用程序使用OQL
2、O-R Mapping解释或者编译OQL
3、对象数据库负责数据处理

如果O-R Mapping使用解释的方式处理OQL,则会包括产生SQL、组装对象等操作。效率通常都不会很好,现在的O-R Mapping产品基本都是基于解释的方式处理。

如果O-R Mapping使用编译的方式,可以优化产生SQL,动态创建SQL存储过程,减少SQL交互的过程,能够获得很好的性能,可以做到比绝
大多数人直接使用SQL性能都要好。我曾经做过一个实验性的实现,取得很好的效果,可惜由于种种原因,没有继续下去。

我认为,下一代的O-R Mapping产品,都将会采用编译的方式,因为在很多情形下,特别是复杂对象的处理方面,可以有大幅度的性能提升,在某些场景下,可以数倍甚至数十倍的性能提升。

一直不是很看好Hibernate,因为其主要作者gavin对编译技术不了解,2.x版本的HQL语法很搞笑,实现也是很搞笑的,简单的文本替换,看到让人目瞪口呆。3.0之后加入了HQL的AST(抽象语法树),但这不是他本人的做的,其他爱好者加入进来,3.1还是没有很好融合进来。之后的版本我就没有继续关注了。

我觉得O-R Mapping完全就是一种编译技术,不懂编译技术的人去作这件事清总会有些不妥。这不单是Hibernate的问题,也是其他O-R Mapping产品的问题。


我的观点:
1、Java、C#、C++等语言在处理关系数据方面没有优势。SQL是关系数据处理的领域专用语言(DSL),更适合处理关系数据,提供强大的功能。
2、关系数据是主流,希望应用开发人员使用O-R Mapping,而不懂关系数据库,这是不现实的。
3、O-R Mapping技术还有很大发展空间,以后在功能、性能等方面都会有重大提升,最终成熟。


温少 2007-04-23 08:18 发表评论

文章来源:http://www.cnblogs.com/jobs/archive/2007/04/23/723297.html
posted @ 2007-04-23 08:23 温少的日志 阅读(1159) | 评论 (2)编辑 收藏
 

在操作系统中,有两种不同的方法提供线程支持:用户层的用户线程,或内核层的内核线程。

其中用户线程在内核之上支持,并在用户层通过线程库来实现。不需要用户态/核心态切换,速度快。操作系统内核不知道多线程的存在,因此一个线程阻塞将使得整个进程(包括它的所有线程)阻塞。由于这里的处理器时间片分配是以进程为基本单位,所以每个线程执行的时间相对减少。

内核线程由操作系统直接支持。由操作系统内核创建、调度和管理。内核维护进程及线程的上下文信息以及线程切换。一个内核线程由于I/O操作而阻塞,不会影响其它线程的运行。

Java线程的实现是怎样的呢?我们通过SUN Java 6的源码了解其在Windows和Linux下的实现。

在Windows下的实现,os_win32.cpp中
// Allocate and initialize a new OSThread
bool os::create_thread(Thread* thread, ThreadType thr_type, size_t stack_size) {
  unsigned thread_id;

  
// Allocate the OSThread object
  OSThread* osthread = new OSThread(NULL, NULL);
  
if (osthread == NULL) {
    
return false;
  }

  
// Initial state is ALLOCATED but not INITIALIZED
  {
    MutexLockerEx ml(thread
->SR_lock(), Mutex::_no_safepoint_check_flag);
    osthread
->set_state(ALLOCATED);
  }
  
  
// Initialize support for Java interrupts
  HANDLE interrupt_event = CreateEvent(NULL, truefalse, NULL);
  
if (interrupt_event == NULL) {
    delete osthread;
    
return NULL;
  }
  osthread
->set_interrupt_event(interrupt_event);
  osthread
->set_interrupted(false);
  
  thread
->set_osthread(osthread);
  
  
if (stack_size == 0) {
    
switch (thr_type) {
    
case os::java_thread:
      
// Java threads use ThreadStackSize which default value can be changed with the flag -Xss
      if (JavaThread::stack_size_at_create() > 0)
        stack_size 
= JavaThread::stack_size_at_create();
      
break;
    
case os::compiler_thread:
      
if (CompilerThreadStackSize > 0) {
        stack_size 
= (size_t)(CompilerThreadStackSize * K);
        
break;
      } 
// else fall through:
        
// use VMThreadStackSize if CompilerThreadStackSize is not defined
    case os::vm_thread:
    
case os::pgc_thread:
    
case os::cgc_thread:
    
case os::watcher_thread:
      
if (VMThreadStackSize > 0) stack_size = (size_t)(VMThreadStackSize * K);
      
break;
    }
  }

  
// Create the Win32 thread
  
//
  
// Contrary to what MSDN document says, "stack_size" in _beginthreadex()
  
// does not specify stack size. Instead, it specifies the size of
  
// initially committed space. The stack size is determined by
  
// PE header in the executable. If the committed "stack_size" is larger
  
// than default value in the PE header, the stack is rounded up to the
  
// nearest multiple of 1MB. For example if the launcher has default
  
// stack size of 320k, specifying any size less than 320k does not
  
// affect the actual stack size at all, it only affects the initial
  
// commitment. On the other hand, specifying 'stack_size' larger than
  
// default value may cause significant increase in memory usage, because
  
// not only the stack space will be rounded up to MB, but also the
  
// entire space is committed upfront.
  
//
  
// Finally Windows XP added a new flag 'STACK_SIZE_PARAM_IS_A_RESERVATION'
  
// for CreateThread() that can treat 'stack_size' as stack size. However we
  
// are not supposed to call CreateThread() directly according to MSDN
  
// document because JVM uses C runtime library. The good news is that the
  
// flag appears to work with _beginthredex() as well.

#ifndef STACK_SIZE_PARAM_IS_A_RESERVATION
#define STACK_SIZE_PARAM_IS_A_RESERVATION  (0x10000)
#endif

  HANDLE thread_handle 
=
    (HANDLE)_beginthreadex(NULL,
                           (unsigned)stack_size,
                           (unsigned (__stdcall 
*)(void*)) java_start,
                           thread,
                           CREATE_SUSPENDED 
| STACK_SIZE_PARAM_IS_A_RESERVATION,
                           
&thread_id);
  
if (thread_handle == NULL) {
    
// perhaps STACK_SIZE_PARAM_IS_A_RESERVATION is not supported, try again
    
// without the flag.
    thread_handle =
    (HANDLE)_beginthreadex(NULL,
                           (unsigned)stack_size,
                           (unsigned (__stdcall 
*)(void*)) java_start,
                           thread,
                           CREATE_SUSPENDED,
                           
&thread_id);
  }
  
if (thread_handle == NULL) {
    
// Need to clean up stuff we've allocated so far
    CloseHandle(osthread->interrupt_event());
    thread
->set_osthread(NULL);
    delete osthread;
    
return NULL;
  }
  
  Atomic::inc_ptr((intptr_t
*)&os::win32::_os_thread_count);

  
// Store info on the Win32 thread into the OSThread
  osthread->set_thread_handle(thread_handle);
  osthread
->set_thread_id(thread_id);

  
// Initial thread state is INITIALIZED, not SUSPENDED
  {
    MutexLockerEx ml(thread
->SR_lock(), Mutex::_no_safepoint_check_flag);
    osthread
->set_state(INITIALIZED);
  }

  
// The thread is returned suspended (in state INITIALIZED), and is started higher up in the call chain
  return true;
}

可以看出,SUN JVM在Windows下的实现,使用_beginthreadex来创建线程,注释中也说明了为什么不用“Window编程书籍推荐使用”的CreateThread函数。由此看出,Java线程在Window下的实现是使用内核线程。

而在Linux下又是怎样的呢?

在os_linux.cpp文件中的代码摘录如下:

# include <pthread.h>

bool
 os::create_thread(Thread* thread, ThreadType thr_type, size_t stack_size) {
  assert(thread
->osthread() == NULL, "caller responsible");

  
// Allocate the OSThread object
  OSThread* osthread = new OSThread(NULL, NULL);
  
if (osthread == NULL) {
    
return false;
  }

  
// set the correct thread state
  osthread->set_thread_type(thr_type);

  
// Initial state is ALLOCATED but not INITIALIZED
  osthread->set_state(ALLOCATED);

  thread
->set_osthread(osthread);

  
// init thread attributes
  pthread_attr_t attr;
  pthread_attr_init(
&attr);
  pthread_attr_setdetachstate(
&attr, PTHREAD_CREATE_DETACHED);

  
// stack size
  if (os::Linux::supports_variable_stack_size()) {
    
// calculate stack size if it's not specified by caller
    if (stack_size == 0) {
      stack_size 
= os::Linux::default_stack_size(thr_type);

      
switch (thr_type) {
      
case os::java_thread:
        
// Java threads use ThreadStackSize which default value can be changed with the flag -Xss
        if (JavaThread::stack_size_at_create() > 0) stack_size = JavaThread::stack_size_at_create();
        
break;
      
case os::compiler_thread:
        
if (CompilerThreadStackSize > 0) {
          stack_size 
= (size_t)(CompilerThreadStackSize * K);
          
break;
        } 
// else fall through:
          
// use VMThreadStackSize if CompilerThreadStackSize is not defined
      case os::vm_thread: 
      
case os::pgc_thread: 
      
case os::cgc_thread: 
      
case os::watcher_thread: 
        
if (VMThreadStackSize > 0) stack_size = (size_t)(VMThreadStackSize * K);
        
break;
      }
    }

    stack_size 
= MAX2(stack_size, os::Linux::min_stack_allowed);
    pthread_attr_setstacksize(
&attr, stack_size);
  } 
else {
    
// let pthread_create() pick the default value.
  }

  
// glibc guard page
  pthread_attr_setguardsize(&attr, os::Linux::default_guard_size(thr_type));

  ThreadState state;

  {
    
// Serialize thread creation if we are running with fixed stack LinuxThreads
    bool lock = os::Linux::is_LinuxThreads() && !os::Linux::is_floating_stack();
    
if (lock) {
      os::Linux::createThread_lock()
->lock_without_safepoint_check();
    }

    pthread_t tid;
    
int ret = pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread);

    pthread_attr_destroy(
&attr);

    
if (ret != 0) {
      
if (PrintMiscellaneous && (Verbose || WizardMode)) {
        perror(
"pthread_create()");
      }
      
// Need to clean up stuff we've allocated so far
      thread->set_osthread(NULL);
      delete osthread;
      
if (lock) os::Linux::createThread_lock()->unlock();
      
return false;
    }

    
// Store pthread info into the OSThread
    osthread->set_pthread_id(tid);

    
// Wait until child thread is either initialized or aborted
    {
      Monitor
* sync_with_child = osthread->startThread_lock();
      MutexLockerEx ml(sync_with_child, Mutex::_no_safepoint_check_flag);
      
while ((state = osthread->get_state()) == ALLOCATED) {
        sync_with_child
->wait(Mutex::_no_safepoint_check_flag);
      }
    }

    
if (lock) {
      os::Linux::createThread_lock()
->unlock();
    }
  }

  
// Aborted due to thread limit being reached
  if (state == ZOMBIE) {
      thread
->set_osthread(NULL);
      delete osthread;
      
return false;
  }

  
// The thread is returned suspended (in state INITIALIZED),
  
// and is started higher up in the call chain
  assert(state == INITIALIZED, "race condition");
  
return true;
}

Java在Linux下的线程的创建,使用了pthread线程库,而pthread就是一个用户线程库,因此结论是,Java在Linux下是使用用户线程实现的。





温少 2007-04-18 05:28 发表评论
posted @ 2007-04-18 05:28 温少的日志 阅读(905) | 评论 (4)编辑 收藏
 

MessageDigest的选择好多,包括MD2、MD4、MD5、SHA-1、SHA-256、RIPEMD128、RIPEMD160等等。我们如何选择呢?

选择考虑在两个方面:安全、速度。

MD2很安全,但是速度极慢,一般不用。

速度方面,最快的是MD4,MD5比SHA-1快

速度排名:MD4 > MD5 > RIPEMD-128 > SHA-1 > REPEMD-160

按照《应用密码学手册》提供的表格数据为:
MD4 长度 128 相对速度 1
MD5 长度 128 相对速度 0.68
REPEMD-128 长度 128 相对速度 0.39
SHA-1 长度 160 相对速度 0.29
REPEMD-160 长度 160 相对速度 0.24

我亲自测试的结果和《应用密码学手册》提供的数据接近。

MD4已经很早证明有缺陷,很少使用,最流行的是MD5和SHA-1,但MD5和SHA1也被王小云找到碰撞,证实不安全。

传说SHA-1比MD5要安全,但是SHA-1有美国国家安全局的背景,有人怀疑这个算法背后有不可告人的秘密,我也是阴谋论者之一,倾向选择MD5而不是SHA-1。王小云找到SHA-1碰撞之后,可以说传说的谣言破灭了,而且MD5速度要比SHA-1快差不多一倍,没有什么理由选择SHA-1。

----------------------------------

在Java的现实环境中是怎样?

在SUN的JCE实现中,只提供了MD2、MD5、SHA-1,SHA-256等常用的MessageDigest算法。

开源的JCE实现bouncycastle则提供了众多的实现,包括MD整个系列,SHA整个系列,RIPEMD整个系列。

很多的开源项目都带一个bcprov-jdk14.jar的包,可以说bouncycastle应用很广泛。SUN公司的一些项目也用了bouncycastle,例如JXTA。

但实际上,SUN的实现包括了MD4,但你需要这样使用:
MessageDigest md = sun.security.provider.MD4.getInstance();


但是,JDK带实现性能要比bouncycastle性能好得多,相差速度通常超过一倍以上,我测试过MD5、SHA1和MD4,其性能差别都是类似,一倍多。

比较的结论:
bouncycastle开源免费,提供算法多,但速度较慢
SUN JCE提供的实现,包括了流行常用算法,速度很快,同类型算法比bouncycastle要快一倍以上。

----------------------------------

结论:

又要安全又要速度,选择MD5
追求安全,不在意速度,相信传说,不相信阴谋论,选择SHA系列
追求速度,安全次之,可以选择MD4。

----------------------------------
现实例子:
emule采用MD4和SHA-1两种结合使用
apache之类的技术网站,提供下载的文件,同时提供一个校验文件.md5



温少 2007-04-14 17:02 发表评论
posted @ 2007-04-14 17:02 温少的日志 阅读(483) | 评论 (0)编辑 收藏
 
JSR-000203 More New I/O APIs for the Java Platform - Early Draft Review

http://jcp.org/aboutJava/communityprocess/edr/jsr203/index.html


API的Early Draft Review出来了,就意味很快就要真的出来啦!!

以下是其文档的一个Sample

 static class IOTransaction {
      
public ByteBuffer buffer() {  }
      
public long position() {  }
      
public long updatePosition(int transferred) {  }
  }

  
static class WriteHandler implements CompletionHandler<Integer> {

      
public WriteHandler(AsynchronousFileChannel ch) {  }

      
private AsynchronousFileChannel channel() {  }

      
public void completed(IoFuture<Integer> result) {
          
int bytesTransferred;
          
try {
              bytesTransferred 
= result.getNow();
          } 
catch (ExecutionException x) {  }
 
          IOTransaction transaction 
= (IOTransaction)result.attachment();   
          ByteBuffer buffer 
= transaction.buffer();
          
if (buffer.remaining() > 0) {
              
long position = transaction.updatePosition(bytesTransferred);
              channel().write(buffer, position, transaction, 
this);
          }
      }
  }

  FileReference file 
= 
  List
<IOTransaction> transactionList = 
  
  AsynchronousFileChannel ch 
= AsynchronousFileChannel.open(file, OpenFlag.WRITE);

  WriteHandler handler 
= new WriteHandler(ch);

  
for (IOTransaction transaction: transactionList) {
      
// use the transaction as the attachment
      ch.write(transaction.buffer(), transaction.position(), transaction, handler);
  }


就是我最近很需要的东西,一个异步I/O的实现,十分期待中!!



温少 2007-04-14 12:15 发表评论
posted @ 2007-04-14 12:15 温少的日志 阅读(249) | 评论 (0)编辑 收藏
 

不是使用每连接一线程的技术,而是使用多路复用技术。

作了一个分配算法。第一个HTTP Request返回取得ContentLength之后,如果使用多个连接下载,则需要一个分配算法,分配每个Request所对应的Range。分配的部分可能是一个连续的块,例如bytes=100-999,也可能是一些碎块,例如bytes=500-600,700-800,850-999。为此,我做了一个数据结构,其提供的功能类似java.util.BitSet,也支持and、or等操作。

实现了对ContentType为multipart/bytes的HTTP Message Body的解释。如果发送HTTP Request,Range为多个不连续的部分,返回的HTTP Message,就会是multipart,每个part都会包括一个Head和一个Body,需要一个解析器。

下一步就是把HTTP下载加入P2P下载中!


温少 2007-04-12 01:37 发表评论
posted @ 2007-04-12 01:37 温少的日志 阅读(366) | 评论 (0)编辑 收藏
 

最近编码更流畅了。原因包括:

1) 绝大多数时候纯键盘操作,Eclipse 200多个快捷键,我熟练使用绝大部分,编码的过程,如同行云流水般。

2)掌握了更多的解决问题的办法,有了更广的知识面,编码时,信手拈来。最近一年里,掌握了很多知识,包括并发、网络、操作系统等等方面的知识。

3)组织代码的能力更强了,最近对于大型复杂的程序,组织代码的能力更强了,组织程序的能力包括,更好的结构,更好的扩展性,可测试性,可管理性等等。
  a) 在编码的过程中,使得更多的模块可以单独于整个环境独立测试
  b) 精心处理过的LOG,使得代码更容易跟踪,排错。
  c) 复杂的模块,附带监控管理界面,使得排错和优化都更为方便。
  d) 使用制作状态转换表的手段,清晰化复杂的状态处理。在前些年设计/实现工作流引擎时,就开始使用状态转换表描述状态机,但现在面临的状态机复杂。




温少 2007-03-31 02:42 发表评论
posted @ 2007-03-31 02:42 温少的日志 阅读(250) | 评论 (0)编辑 收藏
 

贴图的实现方式为:

1、把剪切板中的图片存在本地的SendingImages目录,存放的格式使用PNG,当然可以其他格式,但是PNG格式更小。
2、使用MD5算法产生一个ImageID。当然可以使用SHA1等其他算法
3、把imageID发送remote peer
4、当remote peer收到imageID时,检查本地ReceivedImage目录,如果已经存在,显示图片,不存在则发送一个RequestImage请求,并在聊天记录中显示一个等待信息(为一个GIF动画)。
5、本地Peer收到RequestImage请求之后,发送图片数据。如果图片大于64K,则分块发送。
6、remote peer收到图像数据之后,进行校验,看是否正确。
7、校验通过后,把图片在聊天面板上显示(替换等待图片)


预定义表情的实现很简单,自定义表情的实现和贴图实现一致,只是少了从剪贴板保存图片的过程。



温少 2007-03-30 22:01 发表评论
posted @ 2007-03-30 22:01 温少的日志 阅读(205) | 评论 (0)编辑 收藏
 
上一篇博客写了我一些关于P2P下载以及平台的思考,有这样的思考,是因为我正在做一件这样的事情。

我介绍一下我正在做的事情吧:

1、基于JXTA,我崇拜Bill Joy,学习JXTA就是因为我崇拜他,之后觉得这个技术很棒。但是JXTA存在一些用户不友好的地方,包括JXTA的ConfigDialog和DialogAuthenticator是十分用户不友好的,我重写了这些部分。虽然是一些无关痛痒的地方,但是可以改变用户体验,提高用户友好性。

2、简单的插件机制,我做了一个简单的插件系统,Application启动之后挨个装载服务,UI也是服务之一,UI也是基于插件的,在微内核框架流行的今天,使用一个简单的插件机制似乎不是太好,等过一段时间之后考虑使用osgi替代之。

3、提供了两个功能,聊天和文件共享下载。这两个功能分别表现为两个JXTA的Service。

4、聊天功能。目前还比较简单,只实现了不带格式的文本聊天,但是我随后会加入带格式的文本聊天,也将会加入类似腾讯QQ那样的贴图支持,自定义表情支持,腾讯QQ的实现很巧妙,但并不困难。四月初的版本就有可能实现之。

5、共享和下载。目前实现了文件和文件夹的共享。其中包括了高级智能错误检测(AICH)等。传输协议参考了BT和emule的协议。在界面中还实现对DragAndDrop支持,从Windows Explore中拖一个文件到目录共享的面板,即开始共享该文件。

6、存储信息采用apache的Derby数据库。我很喜欢Berkeley DB,Berkely DB高效简洁,但是License不开放。我最终还是采用Derby了,采用Derby将会带来一系列好处,SQL支持、JDBC支持等等,License无限制等等。扩展的应用基于其上也十分方便。由于我曾经开发过多数据库支持引擎KSQL,在KSQL上增加支持Derby的翻译是很容易的事情。如此一来,可能存储引擎部分,将有可能扩展到KSQL目前所支持的多种数据库,包括严格测试过的Oracle、DB2、MS SQL Server,还有经过简单测试支持Sybase、PostgreSQL、MySQL。

7、最近的JXTA Java SE 2.5版本,使用了nio来管理连接,也就说,使用了多路复用的技术,使得每个Peer创建大量连接成为可能,例如Windows下默认最大的多路复用支持1024个连接。而Linux下,java nio是使用epoll实现的,并发性能将更好,这对于聚合点来说很重要。普通的Peer部署在Linux下可能较少,但是聚合点部署在Linux完全是可能的。

8、使用Swing做界面,使用Java 6 SE的Swing,做了系统托盘Tray的支持等等。由于Swing的UI设计工具很不稳定,最终完全手工编写UI部分代码,虽然辛苦,但是代码简洁,不同UI Designer生成的那样。

9、我期望4月初发布一个版本,提供一个基本可用的版本。

10、我是从1月初开始学习JXTA的,到现在还不满3个月,其中还包括过年回家休息等等,玩游戏沉迷等等,但总的来说,我对这个学习速度很满意。不过其中感觉最爽的是,在这个过程中,编码时,基本纯键盘操作,不用鼠标,如行云流水一边,十分流畅,工作效率高,人也舒服。




温少 2007-03-25 06:39 发表评论
posted @ 2007-03-25 06:39 温少的日志 阅读(251) | 评论 (0)编辑 收藏
仅列出标题
共8页: 上一页 1 2 3 4 5 6 7 8 下一页