posts - 29, comments - 0, trackbacks - 0, articles - 0
  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

2007年5月28日

(1):  linux command


Mount USB:

mount -t vfat /dev/sda1 /mnt/cdrom


Unmount USB:

umount /mnt/cdrom


copy directory recyle:

cp -r apache-ant-1.7.0 /usr/local


unpackage file:

tar -zxvf apache-ant-1.7.0-bin.tar.gz


ctr+alt+f4 swich to dos interface

alt+f7 return to graphic


find / -name filename


ping local ip : ifconfig





vi usage :

[esc] [insert]

:w write

:q quit



(2):  software install

From the Ant FAQ:

How do you get ant-1.6.x (or any version later than 1.5.2) to work on on RedHat ES 3?

Redhat ES 3.0 comes installed with ant 1.5.2. Even if you have your PATH and ANT_HOME variables set correctly to a later version of ant, you will always be forced to use the preinstalled version.

To use a later version of ant on this OS you could do the following:

$ ant -version
Apache Ant version 1.5.2-23 compiled on November 12 2003
$ su -l
# rpm -e ant ant-libs
# exit
$ hash -r
$ ant -version
Apache Ant version 1.6.2 compiled on July 16 2004

But on my computer similar do like this:
[root@localhost TestAntBuld]# ant -version
Apache Ant version 1.5.2-20 compiled on September 25 2003
[root@localhost root]# sudo -l
User root may run the following commands on this host:
(ALL) ALL
[root@localhost root]# rpm -e ant ant-libs
[root@localhost root]# exit
logout

[root@localhost root]# bash -r
[root@localhost root]# exit
exit
[root@localhost root]# ant -version
Apache Ant version 1.7.0 compiled on December 13 2006

Now, That is ok!

Install Junit
1 unzip the download zip file
2 set path (/etc/profile and /etc/profile.d/java.sh)

Install PostgreSQL
1 gunzip postgresql-8.2.3.tar.gz
tar xf postgresql-8.2.3.tar

This will create a directory postgresql-8.2.3 under the current directory with the PostgreSQL sources. Change into that directory for the rest of the installation procedure.

2 ./configure

3 gmake

4 gmake check

5 gmake install

6 To undo the installation use the command gmake uninstall. However, this will not remove any created directories.

LD_LIBRARY_PATH=/usr/local/pgsql/lib

  export LD_LIBRARY_PATH
8 set enviroment path

PATH=/usr/local/pgsql/bin:$PATH

 export PATH

JUnit Test:

*** You must first compile the java classes,afer that, then you can test it!

run the test:

    *

      To run the test from the console, type:

          java org.junit.runner.JUnitCore junitfaq.SimpleTest

    *

      To run the test with the test runner used in main(), type:

          java junitfaq.SimpleTest

The passing test results in the following textual output:
        .
Time: 0

OK (1 tests)

 (3):  intro_postgreSQL_startup

Before you can do anything, you must initialize a database storage area on disk. We call this a database cluster.
The desired file system location of your database cluster is indicated by the -D option, for example:

$ initdb -D /usr/local/pgsql/data

anyone can access the database, you must start the database server. The database server program is called postgres.  The postgres program must know where to find the data it is supposed to use. This is done with the -D option. Thus, the simplest way to start the server is:

$ postgres -D /usr/local/pgsql/data

which will leave the server running in the foreground. This must be done while logged into the PostgreSQL user account. Without -D, the server will try to use the data directory named by the environment variable PGDATA. If that variable is not provided either, it will fail.

Normally it is better to start postgres in the background. For this, use the usual shell syntax:

$ postgres -D /usr/local/pgsql/data >logfile 2>&1 &





 


posted @ 2007-06-08 09:54 change| 编辑 收藏

测试网速的命令:

ping -n 5 -l 1472 -f www.sina.com.cn


LDAP:

如果你把 ldap 安装为 windows 服务,你可以像我一样启动:

net start OpenLDAP-slapd

net stop OpenLDAP-slapd

ldapadd -x -D "cn=Manager,dc=grid,dc=nwu" -W -f example.ldif

** 注意: 在第一次添加的时候要讲默认的库 添加进去才可以,否则会报错误 Object 找不到!!!
dc=example,dc=com is not there by default, you have to add it as the first entry.
Your when you are trying to add you are assuming dc=example,dc=com is there,
just remove the fred walter and try again

将每一个目录ENTRY看作是一个统一表的一行,然后把属性看作列

FAQ:
1:
windows下的openldap,filter 不能家单引号 如下(去掉单引号)
ldapsearch -x -b dc=grid,dc=nwu (objectclass=*)

2:
用到的objectclass (处在schema中定义) 必须在 slapd.conf 中include进来,否则报错误

3:
dn: cn=JSmith,ou=Managerment,dc=grid,dc=nwu
cn: James Smith,Jim Smith,Jimmy Smith
这里的dn中的cn  和属性中的cn  必须是一样的,否则报错误:alue of naming attribute 'cn' is not present in entry

4:扩展schema
http://blog.csdn.net/wcy1323/archive/2006/04/26/678459.aspx

posted @ 2007-05-28 15:56 change| 编辑 收藏


ThreadLocal通过一个Map来为每个线程都持有一个变量副本。
这个map以当前线程为key。与synchronized相比,ThreadLocal是以空间换时间的策略来实现多线程程序。

synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。
而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,
这样就隔离了多个线程对数据的数据共享。
而Synchronized却正好相反,它用于在多个线程间通信时能够获得数据共享。
Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。
当然ThreadLocal并不能替代synchronized,它们处理不同的问题域。
Synchronized用于实现同步机制,比ThreadLocal更加复杂。

posted @ 2007-05-28 15:53 change| 编辑 收藏

from: :OReilly.JavaScript.The.Definitive.Guide.5th.Edition.Aug.2006

 It turns out that every JavaScript object includes an internal reference to another object, known as its prototype object. Any properties of the prototype appear to be properties of an object for which it is the prototype. Another way of saying this is that a JavaScript object inherits properties from its prototype.

In the previous section, I showed that the new operator creates a new, empty object and then invokes a constructor function as a method of that object. This is not the complete story, however. After creating the empty object, new sets the prototype of that object. The prototype of an object is the value of the prototype property of its constructor function. All functions have a prototype property that is automatically created and initialized when the function is defined. The initial value of the prototype property is an object with a single property. This property is named constructor and refers back to the constructor function with which the prototype is associated. (You may recall the constructor property from Chapter 7 ; this is why every object has a constructor property.) Any properties you add to this prototype object will appear to be properties of objects initialized by the constructor.

 

posted @ 2007-05-28 15:50 change| 编辑 收藏

java 是不直接支持 信号量的,我们必须自己来定义我们所需要的信号量

class Semaphore {
private int count;
public Semaphore(int count) {
this.count = count;
}

public synchronized void acquire() {
while(count == 0) {
try {
wait();
} catch (InterruptedException e) {
//keep trying
}
}
count--;
}

public synchronized void release() {
count++;
notify(); //alert a thread that´s blocking on this semaphore
}
}

对要访问的同步资源进行 同步计数控制,来达到同步访问资源的目的。

posted @ 2007-05-28 15:49 change| 编辑 收藏

线程死锁条件:

1:互斥条件 ,即资源是不能够被共享的。

2:至少有一个进程在使用一个资源却在等待另外一个线程所持有的一个资源

3:资源部能够被进程抢占。

4 :必须有循环的等待

那么要解除死锁,只要让这几个条件中的一个不成立就可以了。例如:

哲学家问题,如果每个哲学家都是先 取左边的筷子,在取右边的筷子,那么很有可能就会出现死锁了,那么我们可以让最后的一个哲学家是先取右边的筷子,然后再取左边的筷子,那么就破坏了循环等待的原则,那么死锁自然也就不会成立了 。当然线程也可以一次获得所有的所需资源来实现了。

posted @ 2007-05-28 15:49 change| 编辑 收藏

 What does volatile do?

This is probably best explained by comparing the effects that volatile and synchronized have on a method. volatile is a field modifier, while synchronized modifies code blocks and methods. So we can specify three variations of a simple accessor using those two keywords:

         int i1;              int geti1() {return i1;}
volatile int i2;              int geti2() {return i2;}
int i3; synchronized int geti3() {return i3;}

geti1() accesses the value currently stored in i1 in the current thread. Threads can have local copies of variables, and the data does not have to be the same as the data held in other threads. In particular, another thread may have updated i1 in it's thread, but the value in the current thread could be different from that updated value. In fact Java has the idea of a "main" memory, and this is the memory that holds the current "correct" value for variables. Threads can have their own copy of data for variables, and the thread copy can be different from the "main" memory. So in fact, it is possible for the "main" memory to have a value of 1 for i1, for thread1 to have a value of 2 for i1 and for thread2 to have a value of 3 for i1 if thread1 and thread2 have both updated i1 but those updated value has not yet been propagated to "main" memory or other threads.

On the other hand, geti2() effectively accesses the value of i2 from "main" memory. A volatile variable is not allowed to have a local copy of a variable that is different from the value currently held in "main" memory. Effectively, a variable declared volatile must have it's data synchronized across all threads, so that whenever you access or update the variable in any thread, all other threads immediately see the same value. Of course, it is likely that volatile variables have a higher access and update overhead than "plain" variables, since the reason threads can have their own copy of data is for better efficiency.

Well if volatile already synchronizes data across threads, what is synchronized for? Well there are two differences. Firstly synchronized obtains and releases locks on monitors which can force only one thread at a time to execute a code block, if both threads use the same monitor (effectively the same object lock). That's the fairly well known aspect to synchronized. But synchronized also synchronizes memory. In fact synchronized synchronizes the whole of thread memory with "main" memory. So executing geti3() does the following:

 

  1. The thread acquires the lock on the monitor for object this (assuming the monitor is unlocked, otherwise the thread waits until the monitor is unlocked).
  2. The thread memory flushes all its variables, i.e. it has all of its variables effectively read from "main" memory (JVMs can use dirty sets to optimize this so that only "dirty" variables are flushed, but conceptually this is the same. See section 17.9 of the Java language specification).
  3. The code block is executed (in this case setting the return value to the current value of i3, which may have just been reset from "main" memory).
  4. (Any changes to variables would normally now be written out to "main" memory, but for geti3() we have no changes.)
  5. The thread releases the lock on the monitor for object this.

So where volatile only synchronizes the value of one variable between thread memory and "main" memory, synchronized synchronizes the value of all variables between thread memory and "main" memory, and locks and releases a monitor to boot. Clearly synchronized is likely to have more overhead than volatile.

posted @ 2007-05-28 15:48 change| 编辑 收藏

 
Visual C++线程同步技术剖析:临界区,时间,信号量,互斥量
xuefeifei 发表于 2006-1-12 10:43:00

摘要: 多线程同步技术是计算机软件开发的重要技术,本文对多线程的各种同步技术的原理和实现进行了初步探讨。

关键词: VC++6.0; 线程同步;临界区;事件;互斥;信号量;

正文

使线程同步

  在程序中使用多线程时,一般很少有多个线程能在其生命期内进行完全独立的操作。更多的情况是一些线程进行某些处理操作,而其他的线程必须对其处理结果进行了解。正常情况下对这种处理结果的了解应当在其处理任务完成后进行。

  如果不采取适当的措施,其他线程往往会在线程处理任务结束前就去访问处理结果,这就很有可能得到有关处理结果的错误了解。例如,多个线程同时访问同一个全局变量,如果都是读取操作,则不会出现问题。如果一个线程负责改变此变量的值,而其他线程负责同时读取变量内容,则不能保证读取到的数据是经过写线程修改后的。

  为了确保读线程读取到的是经过修改的变量,就必须在向变量写入数据时禁止其他线程对其的任何访问,直至赋值过程结束后再解除对其他线程的访问限制。象这种保证线程能了解其他线程任务处理结束后的处理结果而采取的保护措施即为线程同步。

  线程同步是一个非常大的话题,包括方方面面的内容。从大的方面讲,线程的同步可分用户模式的线程同步和内核对象的线程同步两大类。用户模式中线程的同步方法主要有原子访问和临界区等方法。其特点是同步速度特别快,适合于对线程运行速度有严格要求的场合。

  内核对象的线程同步则主要由事件、等待定时器、信号量以及信号灯等内核对象构成。由于这种同步机制使用了内核对象,使用时必须将线程从用户模式切换到内核模式,而这种转换一般要耗费近千个CPU周期,因此同步速度较慢,但在适用性上却要远优于用户模式的线程同步方式。

临界区

  临界区(Critical Section)是一段独占对某些共享资源访问的代码,在任意时刻只允许一个线程对共享资源进行访问。如果有多个线程试图同时访问临界区,那么在有一个线程进入后其他所有试图访问此临界区的线程将被挂起,并一直持续到进入临界区的线程离开。临界区在被释放后,其他线程可以继续抢占,并以此达到用原子方式操作共享资源的目的。

  临界区在使用时以CRITICAL_SECTION结构对象保护共享资源,并分别用EnterCriticalSection()和LeaveCriticalSection()函数去标识和释放一个临界区。所用到的CRITICAL_SECTION结构对象必须经过InitializeCriticalSection()的初始化后才能使用,而且必须确保所有线程中的任何试图访问此共享资源的代码都处在此临界区的保护之下。否则临界区将不会起到应有的作用,共享资源依然有被破坏的可能。

图1 使用临界区保持线程同步

下面通过一段代码展示了临界区在保护多线程访问的共享资源中的作用。通过两个线程来分别对全局变量g_cArray[10]进行写入操作,用临界区结构对象g_cs来保持线程的同步,并在开启线程前对其进行初始化。为了使实验效果更加明显,体现出临界区的作用,在线程函数对共享资源g_cArray[10]的写入时,以Sleep()函数延迟1毫秒,使其他线程同其抢占CPU的可能性增大。如果不使用临界区对其进行保护,则共享资源数据将被破坏(参见图1(a)所示计算结果),而使用临界区对线程保持同步后则可以得到正确的结果(参见图1(b)所示计算结果)。代码实现清单附下:

// 临界区结构对象
CRITICAL_SECTION g_cs;
// 共享资源
char g_cArray[10];
UINT ThreadProc10(LPVOID pParam)
{
 // 进入临界区
 EnterCriticalSection(&g_cs);
 // 对共享资源进行写入操作
 for (int i = 0; i < 10; i++)
 {
  g_cArray[i] = 'a';
  Sleep(1);
 }
 // 离开临界区
 LeaveCriticalSection(&g_cs);
 return 0;
}
UINT ThreadProc11(LPVOID pParam)
{
 // 进入临界区
 EnterCriticalSection(&g_cs);
 // 对共享资源进行写入操作
 for (int i = 0; i < 10; i++)
 {
  g_cArray[10 - i - 1] = 'b';
  Sleep(1);
 }
 // 离开临界区
 LeaveCriticalSection(&g_cs);
 return 0;
}
……
void CSample08View::OnCriticalSection()
{
 // 初始化临界区
 InitializeCriticalSection(&g_cs);
 // 启动线程
 AfxBeginThread(ThreadProc10, NULL);
 AfxBeginThread(ThreadProc11, NULL);
 // 等待计算完毕
 Sleep(300);
 // 报告计算结果
 CString sResult = CString(g_cArray);
 AfxMessageBox(sResult);
}


  在使用临界区时,一般不允许其运行时间过长,只要进入临界区的线程还没有离开,其他所有试图进入此临界区的线程都会被挂起而进入到等待状态,并会在一定程度上影响。程序的运行性能。尤其需要注意的是不要将等待用户输入或是其他一些外界干预的操作包含到临界区。如果进入了临界区却一直没有释放,同样也会引起其他线程的长时间等待。换句话说,在执行了EnterCriticalSection()语句进入临界区后无论发生什么,必须确保与之匹配的LeaveCriticalSection()都能够被执行到。可以通过添加结构化异常处理代码来确保LeaveCriticalSection()语句的执行。虽然临界区同步速度很快,但却只能用来同步本进程内的线程,而不可用来同步多个进程中的线程。

  MFC为临界区提供有一个CCriticalSection类,使用该类进行线程同步处理是非常简单的,只需在线程函数中用CCriticalSection类成员函数Lock()和UnLock()标定出被保护代码片段即可。对于上述代码,可通过CCriticalSection类将其改写如下:

// MFC临界区类对象
CCriticalSection g_clsCriticalSection;
// 共享资源
char g_cArray[10];
UINT ThreadProc20(LPVOID pParam)
{
 // 进入临界区
 g_clsCriticalSection.Lock();
 // 对共享资源进行写入操作
 for (int i = 0; i < 10; i++)
 {
  g_cArray[i] = 'a';
  Sleep(1);
 }
 // 离开临界区
 g_clsCriticalSection.Unlock();
 return 0;
}
UINT ThreadProc21(LPVOID pParam)
{
 // 进入临界区
 g_clsCriticalSection.Lock();
 // 对共享资源进行写入操作
 for (int i = 0; i < 10; i++)
 {
  g_cArray[10 - i - 1] = 'b';
  Sleep(1);
 }
 // 离开临界区
 g_clsCriticalSection.Unlock();
 return 0;
}
……
void CSample08View::OnCriticalSectionMfc()
{
 // 启动线程
 AfxBeginThread(ThreadProc20, NULL);
 AfxBeginThread(ThreadProc21, NULL);
 // 等待计算完毕
 Sleep(300);
 // 报告计算结果
 CString sResult = CString(g_cArray);
 AfxMessageBox(sResult);
}


管理事件内核对象

  在前面讲述线程通信时曾使用过事件内核对象来进行线程间的通信,除此之外,事件内核对象也可以通过通知操作的方式来保持线程的同步。对于前面那段使用临界区保持线程同步的代码可用事件对象的线程同步方法改写如下:

// 事件句柄
HANDLE hEvent = NULL;
// 共享资源
char g_cArray[10];
……
UINT ThreadProc12(LPVOID pParam)
{
 // 等待事件置位
 WaitForSingleObject(hEvent, INFINITE);
 // 对共享资源进行写入操作
 for (int i = 0; i < 10; i++)
 {
  g_cArray[i] = 'a';
  Sleep(1);
 }
 // 处理完成后即将事件对象置位
 SetEvent(hEvent);
 return 0;
}
UINT ThreadProc13(LPVOID pParam)
{
 // 等待事件置位
 WaitForSingleObject(hEvent, INFINITE);
 // 对共享资源进行写入操作
 for (int i = 0; i < 10; i++)
 {
  g_cArray[10 - i - 1] = 'b';
  Sleep(1);
 }
 // 处理完成后即将事件对象置位
 SetEvent(hEvent);
 return 0;
}
……
void CSample08View::OnEvent()
{
 // 创建事件
 hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
 // 事件置位
 SetEvent(hEvent);
 // 启动线程
 AfxBeginThread(ThreadProc12, NULL);
 AfxBeginThread(ThreadProc13, NULL);
 // 等待计算完毕
 Sleep(300);
 // 报告计算结果
 CString sResult = CString(g_cArray);
 AfxMessageBox(sResult);
}


  在创建线程前,首先创建一个可以自动复位的事件内核对象hEvent,而线程函数则通过WaitForSingleObject()等待函数无限等待hEvent的置位,只有在事件置位时WaitForSingleObject()才会返回,被保护的代码将得以执行。对于以自动复位方式创建的事件对象,在其置位后一被WaitForSingleObject()等待到就会立即复位,也就是说在执行ThreadProc12()中的受保护代码时,事件对象已经是复位状态的,这时即使有ThreadProc13()对CPU的抢占,也会由于WaitForSingleObject()没有hEvent的置位而不能继续执行,也就没有可能破坏受保护的共享资源。在ThreadProc12()中的处理完成后可以通过SetEvent()对hEvent的置位而允许ThreadProc13()对共享资源g_cArray的处理。这里SetEvent()所起的作用可以看作是对某项特定任务完成的通知。

  使用临界区只能同步同一进程中的线程,而使用事件内核对象则可以对进程外的线程进行同步,其前提是得到对此事件对象的访问权。可以通过OpenEvent()函数获取得到,其函数原型为:

HANDLE OpenEvent(
 DWORD dwDesiredAccess, // 访问标志
 BOOL bInheritHandle, // 继承标志
 LPCTSTR lpName // 指向事件对象名的指针
);


  如果事件对象已创建(在创建事件时需要指定事件名),函数将返回指定事件的句柄。对于那些在创建事件时没有指定事件名的事件内核对象,可以通过使用内核对象的继承性或是调用DuplicateHandle()函数来调用CreateEvent()以获得对指定事件对象的访问权。在获取到访问权后所进行的同步操作与在同一个进程中所进行的线程同步操作是一样的。

  如果需要在一个线程中等待多个事件,则用WaitForMultipleObjects()来等待。WaitForMultipleObjects()与WaitForSingleObject()类似,同时监视位于句柄数组中的所有句柄。这些被监视对象的句柄享有平等的优先权,任何一个句柄都不可能比其他句柄具有更高的优先权。WaitForMultipleObjects()的函数原型为:

DWORD WaitForMultipleObjects(
 DWORD nCount, // 等待句柄数
 CONST HANDLE *lpHandles, // 句柄数组首地址
 BOOL fWaitAll, // 等待标志
 DWORD dwMilliseconds // 等待时间间隔
);


  参数nCount指定了要等待的内核对象的数目,存放这些内核对象的数组由lpHandles来指向。fWaitAll对指定的这nCount个内核对象的两种等待方式进行了指定,为TRUE时当所有对象都被通知时函数才会返回,为FALSE则只要其中任何一个得到通知就可以返回。dwMilliseconds在饫锏淖饔糜朐赪aitForSingleObject()中的作用是完全一致的。如果等待超时,函数将返回WAIT_TIMEOUT。如果返回WAIT_OBJECT_0到WAIT_OBJECT_0+nCount-1中的某个值,则说明所有指定对象的状态均为已通知状态(当fWaitAll为TRUE时)或是用以减去WAIT_OBJECT_0而得到发生通知的对象的索引(当fWaitAll为FALSE时)。如果返回值在WAIT_ABANDONED_0与WAIT_ABANDONED_0+nCount-1之间,则表示所有指定对象的状态均为已通知,且其中至少有一个对象是被丢弃的互斥对象(当fWaitAll为TRUE时),或是用以减去WAIT_OBJECT_0表示一个等待正常结束的互斥对象的索引(当fWaitAll为FALSE时)。 下面给出的代码主要展示了对WaitForMultipleObjects()函数的使用。通过对两个事件内核对象的等待来控制线程任务的执行与中途退出:

// 存放事件句柄的数组
HANDLE hEvents[2];
UINT ThreadProc14(LPVOID pParam)
{
 // 等待开启事件
 DWORD dwRet1 = WaitForMultipleObjects(2, hEvents, FALSE, INFINITE);
 // 如果开启事件到达则线程开始执行任务
 if (dwRet1 == WAIT_OBJECT_0)
 {
  AfxMessageBox("线程开始工作!");
  while (true)
  {
   for (int i = 0; i < 10000; i++);
   // 在任务处理过程中等待结束事件
   DWORD dwRet2 = WaitForMultipleObjects(2, hEvents, FALSE, 0);
   // 如果结束事件置位则立即终止任务的执行
   if (dwRet2 == WAIT_OBJECT_0 + 1)
    break;
  }
 }
 AfxMessageBox("线程退出!");
 return 0;
}
……
void CSample08View::OnStartEvent()
{
 // 创建线程
 for (int i = 0; i < 2; i++)
  hEvents[i] = CreateEvent(NULL, FALSE, FALSE, NULL);
  // 开启线程
  AfxBeginThread(ThreadProc14, NULL);
  // 设置事件0(开启事件)
  SetEvent(hEvents[0]);
}
void CSample08View::OnEndevent()
{
 // 设置事件1(结束事件)
 SetEvent(hEvents[1]);
}


  MFC为事件相关处理也提供了一个CEvent类,共包含有除构造函数外的4个成员函数PulseEvent()、ResetEvent()、SetEvent()和UnLock()。在功能上分别相当与Win32 API的PulseEvent()、ResetEvent()、SetEvent()和CloseHandle()等函数。而构造函数则履行了原CreateEvent()函数创建事件对象的职责,其函数原型为:

CEvent(BOOL bInitiallyOwn = FALSE, BOOL bManualReset = FALSE, LPCTSTR lpszName = NULL, LPSECURITY_ATTRIBUTES lpsaAttribute = NULL );


  按照此缺省设置将创建一个自动复位、初始状态为复位状态的没有名字的事件对象。封装后的CEvent类使用起来更加方便,图2即展示了CEvent类对A、B两线程的同步过程:

图2 CEvent类对线程的同步过程示意

B线程在执行到CEvent类成员函数Lock()时将会发生阻塞,而A线程此时则可以在没有B线程干扰的情况下对共享资源进行处理,并在处理完成后通过成员函数SetEvent()向B发出事件,使其被释放,得以对A先前已处理完毕的共享资源进行操作。可见,使用CEvent类对线程的同步方法与通过API函数进行线程同步的处理方法是基本一致的。前面的API处理代码可用CEvent类将其改写为:

// MFC事件类对象
CEvent g_clsEvent;
UINT ThreadProc22(LPVOID pParam)
{
 // 对共享资源进行写入操作
 for (int i = 0; i < 10; i++)
 {
  g_cArray[i] = 'a';
  Sleep(1);
 }
 // 事件置位
 g_clsEvent.SetEvent();
 return 0;
}
UINT ThreadProc23(LPVOID pParam)
{
 // 等待事件
 g_clsEvent.Lock();
 // 对共享资源进行写入操作
 for (int i = 0; i < 10; i++)
 {
  g_cArray[10 - i - 1] = 'b';
  Sleep(1);
 }
 return 0;
}
……
void CSample08View::OnEventMfc()
{
 // 启动线程
 AfxBeginThread(ThreadProc22, NULL);
 AfxBeginThread(ThreadProc23, NULL);
 // 等待计算完毕
 Sleep(300);
 // 报告计算结果
 CString sResult = CString(g_cArray);
 AfxMessageBox(sResult);
}

 

  信号量内核对象

  信号量(Semaphore)内核对象对线程的同步方式与前面几种方法不同,它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目。在用CreateSemaphore()创建信号量时即要同时指出允许的最大资源计数和当前可用资源计数。一般是将当前可用资源计数设置为最大资源计数,每增加一个线程对共享资源的访问,当前可用资源计数就会减1,只要当前可用资源计数是大于0的,就可以发出信号量信号。但是当前可用计数减小到0时则说明当前占用资源的线程数已经达到了所允许的最大数目,不能在允许其他线程的进入,此时的信号量信号将无法发出。线程在处理完共享资源后,应在离开的同时通过ReleaseSemaphore()函数将当前可用资源计数加1。在任何时候当前可用资源计数决不可能大于最大资源计数。

图3 使用信号量对象控制资源

下面结合图例3来演示信号量对象对资源的控制。在图3中,以箭头和白色箭头表示共享资源所允许的最大资源计数和当前可用资源计数。初始如图(a)所示,最大资源计数和当前可用资源计数均为4,此后每增加一个对资源进行访问的线程(用黑色箭头表示)当前资源计数就会相应减1,图(b)即表示的在3个线程对共享资源进行访问时的状态。当进入线程数达到4个时,将如图(c)所示,此时已达到最大资源计数,而当前可用资源计数也已减到0,其他线程无法对共享资源进行访问。在当前占有资源的线程处理完毕而退出后,将会释放出空间,图(d)已有两个线程退出对资源的占有,当前可用计数为2,可以再允许2个线程进入到对资源的处理。可以看出,信号量是通过计数来对线程访问资源进行控制的,而实际上信号量确实也被称作Dijkstra计数器。

  使用信号量内核对象进行线程同步主要会用到CreateSemaphore()、OpenSemaphore()、ReleaseSemaphore()、WaitForSingleObject()和WaitForMultipleObjects()等函数。其中,CreateSemaphore()用来创建一个信号量内核对象,其函数原型为:

HANDLE CreateSemaphore(
 LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, // 安全属性指针
 LONG lInitialCount, // 初始计数
 LONG lMaximumCount, // 最大计数
 LPCTSTR lpName // 对象名指针
);


  参数lMaximumCount是一个有符号32位值,定义了允许的最大资源计数,最大取值不能超过4294967295。lpName参数可以为创建的信号量定义一个名字,由于其创建的是一个内核对象,因此在其他进程中可以通过该名字而得到此信号量。OpenSemaphore()函数即可用来根据信号量名打开在其他进程中创建的信号量,函数原型如下:

HANDLE OpenSemaphore(
 DWORD dwDesiredAccess, // 访问标志
 BOOL bInheritHandle, // 继承标志
 LPCTSTR lpName // 信号量名
);


  在线程离开对共享资源的处理时,必须通过ReleaseSemaphore()来增加当前可用资源计数。否则将会出现当前正在处理共享资源的实际线程数并没有达到要限制的数值,而其他线程却因为当前可用资源计数为0而仍无法进入的情况。ReleaseSemaphore()的函数原型为:

BOOL ReleaseSemaphore(
 HANDLE hSemaphore, // 信号量句柄
 LONG lReleaseCount, // 计数递增数量
 LPLONG lpPreviousCount // 先前计数
);


  该函数将lReleaseCount中的值添加给信号量的当前资源计数,一般将lReleaseCount设置为1,如果需要也可以设置其他的值。WaitForSingleObject()和WaitForMultipleObjects()主要用在试图进入共享资源的线程函数入口处,主要用来判断信号量的当前可用资源计数是否允许本线程的进入。只有在当前可用资源计数值大于0时,被监视的信号量内核对象才会得到通知。

  信号量的使用特点使其更适用于对Socket(套接字)程序中线程的同步。例如,网络上的HTTP服务器要对同一时间内访问同一页面的用户数加以限制,这时可以为没一个用户对服务器的页面请求设置一个线程,而页面则是待保护的共享资源,通过使用信号量对线程的同步作用可以确保在任一时刻无论有多少用户对某一页面进行访问,只有不大于设定的最大用户数目的线程能够进行访问,而其他的访问企图则被挂起,只有在有用户退出对此页面的访问后才有可能进入。下面给出的示例代码即展示了类似的处理过程:

// 信号量对象句柄
HANDLE hSemaphore;
UINT ThreadProc15(LPVOID pParam)
{
 // 试图进入信号量关口
 WaitForSingleObject(hSemaphore, INFINITE);
 // 线程任务处理
 AfxMessageBox("线程一正在执行!");
 // 释放信号量计数
 ReleaseSemaphore(hSemaphore, 1, NULL);
 return 0;
}
UINT ThreadProc16(LPVOID pParam)
{
 // 试图进入信号量关口
 WaitForSingleObject(hSemaphore, INFINITE);
 // 线程任务处理
 AfxMessageBox("线程二正在执行!");
 // 释放信号量计数
 ReleaseSemaphore(hSemaphore, 1, NULL);
 return 0;
}
UINT ThreadProc17(LPVOID pParam)
{
 // 试图进入信号量关口
 WaitForSingleObject(hSemaphore, INFINITE);
 // 线程任务处理
 AfxMessageBox("线程三正在执行!");
 // 释放信号量计数
 ReleaseSemaphore(hSemaphore, 1, NULL);
 return 0;
}
……
void CSample08View::OnSemaphore()
{
 // 创建信号量对象
 hSemaphore = CreateSemaphore(NULL, 2, 2, NULL);
 // 开启线程
 AfxBeginThread(ThreadProc15, NULL);
 AfxBeginThread(ThreadProc16, NULL);
 AfxBeginThread(ThreadProc17, NULL);
}


图4 开始进入的两个线程

图5 线程二退出后线程三才得以进入

上述代码在开启线程前首先创建了一个初始计数和最大资源计数均为2的信号量对象hSemaphore。即在同一时刻只允许2个线程进入由hSemaphore保护的共享资源。随后开启的三个线程均试图访问此共享资源,在前两个线程试图访问共享资源时,由于hSemaphore的当前可用资源计数分别为2和1,此时的hSemaphore是可以得到通知的,也就是说位于线程入口处的WaitForSingleObject()将立即返回,而在前两个线程进入到保护区域后,hSemaphore的当前资源计数减少到0,hSemaphore将不再得到通知,WaitForSingleObject()将线程挂起。直到此前进入到保护区的线程退出后才能得以进入。图4和图5为上述代脉的运行结果。从实验结果可以看出,信号量始终保持了同一时刻不超过2个线程的进入。

  在MFC中,通过CSemaphore类对信号量作了表述。该类只具有一个构造函数,可以构造一个信号量对象,并对初始资源计数、最大资源计数、对象名和安全属性等进行初始化,其原型如下:

CSemaphore( LONG lInitialCount = 1, LONG lMaxCount = 1, LPCTSTR pstrName = NULL, LPSECURITY_ATTRIBUTES lpsaAttributes = NULL );


  在构造了CSemaphore类对象后,任何一个访问受保护共享资源的线程都必须通过CSemaphore从父类CSyncObject类继承得到的Lock()和UnLock()成员函数来访问或释放CSemaphore对象。与前面介绍的几种通过MFC类保持线程同步的方法类似,通过CSemaphore类也可以将前面的线程同步代码进行改写,这两种使用信号量的线程同步方法无论是在实现原理上还是从实现结果上都是完全一致的。下面给出经MFC改写后的信号量线程同步代码:

// MFC信号量类对象
CSemaphore g_clsSemaphore(2, 2);
UINT ThreadProc24(LPVOID pParam)
{
 // 试图进入信号量关口
 g_clsSemaphore.Lock();
 // 线程任务处理
 AfxMessageBox("线程一正在执行!");
 // 释放信号量计数
 g_clsSemaphore.Unlock();
 return 0;
}
UINT ThreadProc25(LPVOID pParam)
{
 // 试图进入信号量关口
 g_clsSemaphore.Lock();
 // 线程任务处理
 AfxMessageBox("线程二正在执行!");
 // 释放信号量计数
 g_clsSemaphore.Unlock();
 return 0;
}
UINT ThreadProc26(LPVOID pParam)
{
 // 试图进入信号量关口
 g_clsSemaphore.Lock();
 // 线程任务处理
 AfxMessageBox("线程三正在执行!");
 // 释放信号量计数
 g_clsSemaphore.Unlock();
 return 0;
}
……
void CSample08View::OnSemaphoreMfc()
{
 // 开启线程
 AfxBeginThread(ThreadProc24, NULL);
 AfxBeginThread(ThreadProc25, NULL);
 AfxBeginThread(ThreadProc26, NULL);
}

  互斥内核对象

  互斥(Mutex)是一种用途非常广泛的内核对象。能够保证多个线程对同一共享资源的互斥访问。同临界区有些类似,只有拥有互斥对象的线程才具有访问资源的权限,由于互斥对象只有一个,因此就决定了任何情况下此共享资源都不会同时被多个线程所访问。当前占据资源的线程在任务处理完后应将拥有的互斥对象交出,以便其他线程在获得后得以访问资源。与其他几种内核对象不同,互斥对象在操作系统中拥有特殊代码,并由操作系统来管理,操作系统甚至还允许其进行一些其他内核对象所不能进行的非常规操作。为便于理解,可参照图6给出的互斥内核对象的工作模型:

图6 使用互斥内核对象对共享资源的保护
图(a)中的箭头为要访问资源(矩形框)的线程,但只有第二个线程拥有互斥对象(黑点)并得以进入到共享资源,而其他线程则会被排斥在外(如图(b)所示)。当此线程处理完共享资源并准备离开此区域时将把其所拥有的互斥对象交出(如图(c)所示),其他任何一个试图访问此资源的线程都有机会得到此互斥对象。

  以互斥内核对象来保持线程同步可能用到的函数主要有CreateMutex()、OpenMutex()、ReleaseMutex()、WaitForSingleObject()和WaitForMultipleObjects()等。在使用互斥对象前,首先要通过CreateMutex()或OpenMutex()创建或打开一个互斥对象。CreateMutex()函数原型为:

HANDLE CreateMutex(
 LPSECURITY_ATTRIBUTES lpMutexAttributes, // 安全属性指针
 BOOL bInitialOwner, // 初始拥有者
 LPCTSTR lpName // 互斥对象名
);

  参数bInitialOwner主要用来控制互斥对象的初始状态。一般多将其设置为FALSE,以表明互斥对象在创建时并没有为任何线程所占有。如果在创建互斥对象时指定了对象名,那么可以在本进程其他地方或是在其他进程通过OpenMutex()函数得到此互斥对象的句柄。OpenMutex()函数原型为:

HANDLE OpenMutex(
 DWORD dwDesiredAccess, // 访问标志
 BOOL bInheritHandle, // 继承标志
 LPCTSTR lpName // 互斥对象名
);

  当目前对资源具有访问权的线程不再需要访问此资源而要离开时,必须通过ReleaseMutex()函数来释放其拥有的互斥对象,其函数原型为:

BOOL ReleaseMutex(HANDLE hMutex);

  其唯一的参数hMutex为待释放的互斥对象句柄。至于WaitForSingleObject()和WaitForMultipleObjects()等待函数在互斥对象保持线程同步中所起的作用与在其他内核对象中的作用是基本一致的,也是等待互斥内核对象的通知。但是这里需要特别指出的是:在互斥对象通知引起调用等待函数返回时,等待函数的返回值不再是通常的WAIT_OBJECT_0(对于WaitForSingleObject()函数)或是在WAIT_OBJECT_0到WAIT_OBJECT_0+nCount-1之间的一个值(对于WaitForMultipleObjects()函数),而是将返回一个WAIT_ABANDONED_0(对于WaitForSingleObject()函数)或是在WAIT_ABANDONED_0到WAIT_ABANDONED_0+nCount-1之间的一个值(对于WaitForMultipleObjects()函数)。以此来表明线程正在等待的互斥对象由另外一个线程所拥有,而此线程却在使用完共享资源前就已经终止。除此之外,使用互斥对象的方法在等待线程的可调度性上同使用其他几种内核对象的方法也有所不同,其他内核对象在没有得到通知时,受调用等待函数的作用,线程将会挂起,同时失去可调度性,而使用互斥的方法却可以在等待的同时仍具有可调度性,这也正是互斥对象所能完成的非常规操作之一。

  在编写程序时,互斥对象多用在对那些为多个线程所访问的内存块的保护上,可以确保任何线程在处理此内存块时都对其拥有可靠的独占访问权。下面给出的示例代码即通过互斥内核对象hMutex对共享内存快g_cArray[]进行线程的独占访问保护。下面给出实现代码清单:

// 互斥对象
HANDLE hMutex = NULL;
char g_cArray[10];
UINT ThreadProc18(LPVOID pParam)
{
 // 等待互斥对象通知
 WaitForSingleObject(hMutex, INFINITE);
 // 对共享资源进行写入操作
 for (int i = 0; i < 10; i++)
 {
  g_cArray[i] = 'a';
  Sleep(1);
 }
 // 释放互斥对象
 ReleaseMutex(hMutex);
 return 0;
}
UINT ThreadProc19(LPVOID pParam)
{
 // 等待互斥对象通知
 WaitForSingleObject(hMutex, INFINITE);
 // 对共享资源进行写入操作
 for (int i = 0; i < 10; i++)
 {
  g_cArray[10 - i - 1] = 'b';
  Sleep(1);
 }
 // 释放互斥对象
 ReleaseMutex(hMutex);
 return 0;
}
……
void CSample08View::OnMutex()
{
 // 创建互斥对象
 hMutex = CreateMutex(NULL, FALSE, NULL);
 // 启动线程
 AfxBeginThread(ThreadProc18, NULL);
 AfxBeginThread(ThreadProc19, NULL);
 // 等待计算完毕
 Sleep(300);
 // 报告计算结果
 CString sResult = CString(g_cArray);
 AfxMessageBox(sResult);
}

  互斥对象在MFC中通过CMutex类进行表述。使用CMutex类的方法非常简单,在构造CMutex类对象的同时可以指明待查询的互斥对象的名字,在构造函数返回后即可访问此互斥变量。CMutex类也是只含有构造函数这唯一的成员函数,当完成对互斥对象保护资源的访问后,可通过调用从父类CSyncObject继承的UnLock()函数完成对互斥对象的释放。CMutex类构造函数原型为:

CMutex( BOOL bInitiallyOwn = FALSE, LPCTSTR lpszName = NULL, LPSECURITY_ATTRIBUTES lpsaAttribute = NULL );

  该类的适用范围和实现原理与API方式创建的互斥内核对象是完全类似的,但要简洁的多,下面给出就是对前面的示例代码经CMutex类改写后的程序实现清单:

// MFC互斥类对象
CMutex g_clsMutex(FALSE, NULL);
UINT ThreadProc27(LPVOID pParam)
{
 // 等待互斥对象通知
 g_clsMutex.Lock();
 // 对共享资源进行写入操作
 for (int i = 0; i < 10; i++)
 {
  g_cArray[i] = 'a';
  Sleep(1);
 }
 // 释放互斥对象
 g_clsMutex.Unlock();
 return 0;
}
UINT ThreadProc28(LPVOID pParam)
{
 // 等待互斥对象通知
 g_clsMutex.Lock();
 // 对共享资源进行写入操作
 for (int i = 0; i < 10; i++)
 {
  g_cArray[10 - i - 1] = 'b';
  Sleep(1);
 }
 // 释放互斥对象
 g_clsMutex.Unlock();
 return 0;
}
……
void CSample08View::OnMutexMfc()
{
 // 启动线程
 AfxBeginThread(ThreadProc27, NULL);
 AfxBeginThread(ThreadProc28, NULL);
 // 等待计算完毕
 Sleep(300);
 // 报告计算结果
 CString sResult = CString(g_cArray);
 AfxMessageBox(sResult);
}

  小结

  线程的使用使程序处理更够更加灵活,而这种灵活同样也会带来各种不确定性的可能。尤其是在多个线程对同一公共变量进行访问时。虽然未使用线程同步的程序代码在逻辑上或许没有什么问题,但为了确保程序的正确、可靠运行,必须在适当的场合采取线程同步措施。

posted @ 2007-05-28 15:48 change| 编辑 收藏

如何使一个类只能够有一个对象 呢,我们知道这样的类的构造函数是必须为私有的,否则用户就可以任意多的生产该类的对象了。那么具体应该怎么实现呢,有两种方式:一种是给类一个该类型的 static 成员对象,每次都调用一个get方法返回该对象,这也就是所谓的饿汉模式;

public class EagerSingleton {
 String name;
 private static final EagerSingleton m_instance = new EagerSingleton();
 
 private EagerSingleton()
 {
  name ="wqh";
 }
 public static EagerSingleton getInstance()
 {
  return m_instance;
 }
 
 public static void main(String[] args) {
  EagerSingleton es ;
  es = EagerSingleton.getInstance();
  System.out.println(es.name);
 }
}

在有一种呢就是在该类型里面先声明一个对象,如何通过一个同步的static方法每次返回同一个 对象,这也就是 所谓的懒汉模式,如下:

public class LazySingleton {
 private static LazySingleton m_instance = null;
 private LazySingleton()
 {
 
 }
 synchronized public  static LazySingleton getInstance()
 {
  if(m_instance == null)
  {
   m_instance = new LazySingleton();
  }
  return m_instance;
 }
 
 public static void main(String[] args) {
  LazySingleton ls;
  ls = LazySingleton.getInstance();
  System.out.println(ls.getClass());
 }
}

  

posted @ 2007-05-28 15:47 change| 编辑 收藏

List (interface)  次序是List 的最重要的特点。它确保维护元素特定的顺序。

ArrayList 是由数组实现的 list , 他容许对元素进行快速的随机访问,但是插入与移除 的速度很慢。ListIterator 只应该用来从后往前 遍历 ArrayList ,而不能够做移除。

LinkedList 对顺序访问进行了优化,向List 插入与移除的开销并不大,随机访问则相对较慢。

Set (interface)存入Set 的每个元素都必须是唯一的,不保存重复元素。加入对象必须定义eqauls()方法保证唯一性。不保证维护元素的次序。

HsahSet 为快速查找而设计的 Set 存入HashSet的对象必须定义 hsahCode()方法

TreeSet 保持次序的Set,底层微树结构,使用他可以从Set中提取有序的序列

LinkedHashSet 具有hashSet 的查询速度,且内部使用链表维护元素的顺序(插入的次序)

Map(interface) 维护键值对的关联性。

HashMap 基于散列表的实现(取代HashTable),插入和查询键值对的开销固定,可以通过构造器设置容量和负载因子以调整容器性能。

LinkedHashMap 类似HashMap ,但是叠带遍历时取得 键值对 的顺序是插入的次序或者是 最近最少使用的次序。使用链表维护内部次序。

TreeMap基于红黑树实现,所得到的结果是经过排序的。

posted @ 2007-05-28 15:46 change| 编辑 收藏

摘自:《高质量的 C++编程》 

 

8.2.1重载与覆盖
    成员函数被重载的特征:
1)相同的范围(在同一个类中);
2)函数名字相同;
3)参数不同;
4)virtual关键字可有可无。
    覆盖是指派生类函数覆盖基类函数,特征是:
1)不同的范围(分别位于派生类与基类);
2)函数名字相同;
3)参数相同;
4)基类函数必须有virtual关键字。
 
 
“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下:
1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。
(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)。
 
 
如下示例程序中:
1)函数Derived::f(float)覆盖了Base::f(float)。
2)函数Derived::g(int)隐藏了Base::g(float),而不是重载。
3)函数Derived::h(float)隐藏了Base::h(float),而不是覆盖。
 
#include <iostream.h>
    class Base
{
public:
    virtual void f(float x){ cout << "Base::f(float) " << x << endl; }
void g(float x){ cout << "Base::g(float) " << x << endl; }
            void h(float x){ cout << "Base::h(float) " << x << endl; }
};
    class Derived : public Base
{
public:
    virtual void f(float x){ cout << "Derived::f(float) " << x << endl; }
void g(int x){ cout << "Derived::g(int) " << x << endl; }
            void h(float x){ cout << "Derived::h(float) " << x << endl; }
};

  
void main(void)
{
Derived d;
Base *pb = &d;
Derived *pd = &d;
// Good : behavior depends solely on type of the object
pb->f(3.14f); // Derived::f(float) 3.14
pd->f(3.14f); // Derived::f(float) 3.14
 
// Bad : behavior depends on type of the pointer
pb->g(3.14f); // Base::g(float) 3.14
pd->g(3.14f); // Derived::g(int) 3        (surprise!)
 
// Bad : behavior depends on type of the pointer
//行为(即方法的调用)依赖于指针的类型
pb->h(3.14f); // Base::h(float) 3.14      (surprise!)
pd->h(3.14f); // Derived::h(float) 3.14
}

 

posted @ 2007-05-28 15:45 change| 编辑 收藏

引用就是别名。
引用的一些规则如下:
1)引用被创建的同时必须被初始化(指针则可以在任何时候被初始化)。
2)不能有NULL引用,引用必须与合法的存储单元关联(指针则可以是NULL)。

(3)一旦引用被初始化,就不能改变引用的关系(指针则可以随时改变所指的对象)。

“引用传递”的性质象“指针传递”(能够改变原来的参数值),而书写方式象“值传递”。

posted @ 2007-05-28 15:45 change| 编辑 收藏

  最近找工作,几乎所有的公司有要考C/C++ ,没有办法,呵呵~~~~只有慢慢的开始 拾起 C++ 来,好久没有弄过C++ 了,基本语法都忘得差不多了,呵呵~~~今天看了《高质量的 C++编程》,现摘下一些话,已备忘记查找。

  

C++内存分配方式
内存分配方式有三种:
(1)      从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。
(2)      在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
(3)      从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。
EG:
           用函数返回值来传递动态内存
 
char *GetMemory3(int num)
{
    char *p = (char *)malloc(sizeof(char) * num);
    return p;
}
void Test3(void)
{
    char *str = NULL;
    str = GetMemory3(100); 
    strcpy(str, "hello");
    cout<< str << endl;
    free(str); 
}

 但是下面这种事有问题的:

    
char *GetString(void)
{
    char p[] = "hello world";
    return p;   // 编译器将提出警告
}
void Test4(void)
{
char *str = NULL;
str = GetString(); // str 的内容是垃圾
cout<< str << endl;
}

 

这里强调不要用return语句返回指向“栈内存”的指针,因为该内存在函数结束时自动消亡,如上面的示例。  用调试器逐步跟踪Test4,发现执行str = GetString语句后str不再是NULL指针,但是str的内容不是“hello world”而是垃圾。

 

如果把示例7-4-4改写成示例7-4-5,会怎么样?
 

char *GetString2(void)
{
    char *p = "hello world";
    return p;
}
void Test5(void)
{
    char *str = NULL;
    str = GetString2();
    cout<< str << endl;
}

示例7-4-5 return语句返回常量字符串
 

函数Test5运行虽然不会出错,但是函数GetString2的设计概念却是错误的。因为GetString2内的“hello world”是常量字符串,位于静态存储区,它在程序生命期内恒定不变。无论什么时候调用GetString2,它返回的始终是同一个“只读”的内存块

 

posted @ 2007-05-28 15:44 change| 编辑 收藏

在jxta里面,所有的资源都是通过广告来发布的,这里的服务业不例外,在这里的服务  发布里面有两个很总要的概念,

• ModuleClassAdvertisement— defines the service class; its main purpose is to formally document the
existence of a module class. It is uniquely identified by a ModuleClassID.
• ModuleSpecAdvertisement — defines a service specification; uniquely identified by a ModuleSpecID.
Its main purpose is to provide references to the documentation needed in order to create conforming
implementations of that specification. A secondary use is to make running instances usable remotely,
by publishing any or all of the following:
• PipeAdvertisement
• ModuleSpecID of a proxy module
• ModuleSpecID of an authenticator module
• ModuleImplAdvertisement — defines an implementation of a given service specification. 

这里的 ModuleClassAdvertisement  仅仅用来告知服务的存在,对等点若是需要访问该服务的话,还需要发现与之关联的 ModuleSpecAdvertisement  广告信息。

而这里的 ModuleSpecAdvertisement  则包含了 对等点节点 要访问该服务所需要的所有相关信息,比如:管道广告信息,通过它才能够连接上所需要的服务。

服务端的代码示例大抵如下:

创建发布 ModuleClassAdvertisement  :

ModuleClassAdvertisement mcadv = (ModuleClassAdvertisement)AdvertisementFactory.newAdvertisement(ModuleClassAdvertisement.getAdvertisementType());
   mcadv.setName("JXTAMOD:JXTA-EX1");
   mcadv.setDescription("Tutorial example to use JXTA module advertisement Framework");
   ModuleClassID mcID = IDFactory.newModuleClassID();
   mcadv.setModuleClassID(mcID);//通过mcID来建立ModuleClassAdvertisement 与ModuleSpecAdvertisement 的联系

discovery.publish(mcadv);
 discovery.remotePublish(mcadv);

创建发布 ModuleSpecAdvertisement :

ModuleSpecAdvertisement mdadv = (ModuleSpecAdvertisement)AdvertisementFactory.newAdvertisement(ModuleSpecAdvertisement.getAdvertisementType());

mdadv.setName("JXTASPEC:JXTA-EX1");
   mdadv.setVersion("Version 1.0");
   mdadv.setCreator("sun.com");
   mdadv.setModuleSpecID(IDFactory.newModuleSpecID(mcID));
   mdadv.setSpecURI("http://www.jxta.org/Ex1");

PipeAdvertisement pipeadv = null;
   try {
    FileInputStream is = new FileInputStream("pipeserver.adv");
    pipeadv = (PipeAdvertisement)AdvertisementFactory.newAdvertisement(MimeMediaType.XMLUTF8, is);
    is.close();
   } catch (Exception e) {
    System.out.println("failed to read/parse pipe advertisement");
   }

mdadv.setPipeAdvertisement(pipeadv);

discovery.publish(mdadv);
 discovery.remotePublish(mdadv);
  myPipe = pipes.createInputPipe(pipeadv);

在客户端,通过不断的查找广告(分本地查找和远端查找)来 获取 所需要服务的广告信息,通过它就可以获取 管道信息 来创建管道以达到通讯的目的。客户端代码示例大抵如下:

Enumeration en = null;
  while (true) {
   try {
    /* let's look first in our local cache to see if we have it! We try to discover an adverisement which as the (Name, JXTA-EX1) tag value
    en = discovery.getLocalAdvertisements(DiscoveryService.ADV,"Name","JXTASPEC:JXTA-EX1");
    //  Ok we got something in our local cache does not
    //  need to go further!
    if ((en != null) && en.hasMoreElements()) {
     break;
    }
    //  nothing in the local cache?, let's remotely query
    //  for the service advertisement.
    discovery.getRemoteAdvertisements(null,DiscoveryService.ADV,"Name","JXTASPEC:JXTA-EX1",1, null);
    //  The discovery is asynchronous as we do not know
    //  how long is going to take
    try { // sleep as much as we want. Yes we
     //  should implement asynchronous listener pipe...
     Thread.sleep(2000);
    } catch (Exception e) {}
   } catch (IOException e) {
    //  found nothing! move on
   }
   System.out.print(".");
  }
  System.out.println("we found the service advertisement");
  //  Ok get the service advertisement as a Spec Advertisement
  ModuleSpecAdvertisement mdsadv = (ModuleSpecAdvertisement)en.nextElement();
  try {
   //  let's print the advertisement as a plain text document
   StructuredTextDocument doc = (StructuredTextDocument)mdsadv.getDocument(MimeMediaType.TEXT_DEFAULTENCODING);
   StringWriter out = new StringWriter();
   doc.sendToWriter(out);
   System.out.println(out.toString());
   out.close();
   //  we can find the pipe to connect to the service
   //  in the advertisement.
   PipeAdvertisement pipeadv = mdsadv.getPipeAdvertisement();
   //  Ok we have our pipe advertiseemnt to talk to the service
   //  create the output pipe endpoint to connect  to the server
    myPipe = pipes.createOutputPipe(pipeadv, 10000);
   }

   //  send the message to the service pipe
   myPipe.send (msg);

posted @ 2007-05-28 15:41 change| 编辑 收藏

JXTA 双向通讯 可以通过 JxtaServerSocket /JxtaSocket和 JxtaServerPipe/JxtaBiDiPipe 来实现 其实现的过程非常的类是我们做FTP的时候所采用的ServerSocket/Socket机制,也就是服务断监听客户端连接的原理。以JxtaServerPipe为例,在服务端:

serverPipe = new JxtaServerPipe(eg.netPeerGroup,eg.pipeAdv);

serverPipe.setPipeTimeout(0);然后就是服务端的循环监听客户端的连接

while (true) {
   try {
    JxtaBiDiPipe bipipe = serverPipe.accept();
    if (bipipe != null ) {
     System.out.println("JxtaBidiPipe accepted,sending 100 messages to the other end");
     //Send a 100 messages
     sendTestMessages(bipipe);
    }
   } catch (Exception e) {
     }
  }

在客户端则是通过JxtaBiDiPipe 来进行连接服务断的操作:pipe = new JxtaBiDiPipe();

pipe.connect(eg.netPeerGroup,null,eg.pipeAdv,
    180000,
    // register as a message listener
    eg);当有消息来得时候就会触发 pipeMsgEvent(PipeMsgEvent event)事件

posted @ 2007-05-28 15:41 change| 编辑 收藏

最近在JXTA的官方网站上面下载了一份JxtaProgGuide看了看,练习了一下上面的示例程序~~~~大抵上感觉的编程的模式就是:

//Method to start the JXTA platform.

NetPeerGroupFactory factory  = new NetPeerGroupFactory();//这是默认的创建的一个组。
        netPeerGroup = factory.getInterface(); 

然后就是获取相应的服务如发现服务(用于发现和发布广告,那么什么是广告呢?

Advertisements 就是:
All JXTA network resources— such as peers, peer groups, pipes, and services —are represented by an
advertisement. Advertisements are language-neutral meta-data structures represented as XML documents. The
JXTAprotocols use advertisements to describe and publish the existence of a peer resources. Peers discover
resources by searching for their corresponding advertisements, and may cache any discovered advertisements
locally.),管道服务(用于创建IN/OUT管道来接发消息,这里创建OutPipe管道会触发outputPipeEvent(OutputPipeEvent event) 事件,而当 Inpipe 管道有消息到来的时候会触发pipeMsgEvent(PipeMsgEvent event)事件 ,而这里In/Out 管道间的联系则就是广告的用处了,它通过PipeID标示出所用的管道来建立他们之间的联系而不至于混乱。对等点间的通讯就要依赖于它了)

discovery = netPeerGroup.getDiscoveryService();
 rdv = netPeerGroup.getRendezVousService();

然后是通过所获取的服务来注册监听器在通过发现事件来获取一个广告,或者是直接通过服务来获取一个广告,总之目的就是要获取一个所要找的广告 。如监听:

discovery.addDiscoveryListener(this);此时需要implements DiscoveryListener接口,

实现里面的 discoveryEvent(DiscoveryEvent ev) 方法,然后通过 DiscoveryEvent  获取广告

DiscoveryResponseMsg res = ev.getResponse();
  // Get the responding peer's advertisement
  PeerAdvertisement peerAdv = res.getPeerAdvertisement();

或者是直接通过服务来获取一个广告

discovery.getRemoteAdvertisements(null, DiscoveryService.GROUP, null, null, 5);

在要不就是i通过一个发现服务来发布一个广告,这里的发布广告分本地发布和远程发布

discoveryService.publish(Adv,PeerGroup.DEFAULT_LIFETIME,PeerGroup.DEFAULT_EXPIRATION);
 discoveryService.remotePublish(Adv,PeerGroup.DEFAULT_EXPIRATION);

 那么一个对等点如何才能够加入一个Group呢

StructuredDocument creds = null;

// Generate the credentials for the Peer Group
   AuthenticationCredential authCred = new AuthenticationCredential( grp, null, creds );
   // Get the MembershipService from the peer group
   MembershipService membership = grp.getMembershipService();
   // Get the Authenticator from the Authentication creds
   Authenticator auth = membership.apply( authCred );
   // Check if everything is okay to join the group
   if (auth.isReadyForJoin()){
    Credential myCred = membership.join(auth);
    System.out.println("Successfully joined group " + grp.getPeerGroupName());
    // display the credential as a plain text document.
    System.out.println("\nCredential: ");
    StructuredTextDocument doc = (StructuredTextDocument)myCred.getDocument(new MimeMediaType("text/plain"));
    StringWriter out = new StringWriter();
    doc.sendToWriter(out);
    System.out.println(out.toString());
    out.close();
   }

 

posted @ 2007-05-28 15:40 change| 编辑 收藏

最近也算是闲来无事, 于是乎开始玩玩 J2ME 无线编程,找了一本书翻翻,然后下载了一个 ME插件和诺基亚的模拟器 做了几个小例子,发觉其实也没有什么,感觉基本上可以说是windows 窗口编成的一个缩版(新手愚见,高手见效了)。就是所谓的添加一个form(类是面板),在给他添加几个TextField(类是文本框),添加相应的响应事件,然后就是在不同的现实面板间切换,做一些业务上的事情,至于手机上的存储嘛,基本上是依赖于 DataStore 这个类的,用它获取recordID在获取记录等等,也就是通常的数据库操作(增删改查)都依赖于它。至于手机的联网通讯则是依赖于Connector这个对象了,通过它 既可以创建一个Socket连接,也可以创建一个HTTP连接,只是连接的URL 字符串不同罢了。如:

conn =  (HttpConnection)Connector.open(URL.toString());

想想它的难点的话应该是如何的精简代码,高效的利用存储空间,和网络通讯的安全吧。因为这毕竟是一个手持设备的局限性问题。这方面就确实没有什么经验了,希望有高手的经验共享。呵呵~~~我只是感觉她的编程模型还是蛮好理解的。没有什么神秘可言。我翻的那本书比较的老了,也许现在的MIDP2.0 已经有了很大的改观也说不来噢,个人没有怎么了解。不过在J2ME的开发包里面有好多的Demo,但是现在是没有什么时间去研究它了,呵呵~~~以后再说吧,欢迎大家批评指正。

posted @ 2007-05-28 15:40 change| 编辑 收藏

以前的同步操作 基本上都是用到 synchronised 关键字,类似代码如下:

synchronised(obj){

//dosomething...

}来做到同步,

在 JDK5.0  里面有这么一个对象,ReentrantLock,发觉她的加锁编程的方式非常的适合日常的加锁习惯,

EG:

package com.thread.synchronise;

import java.util.concurrent.locks.ReentrantLock;

public class SynchroTest extends Thread{
 private int count = 0;
 private final ReentrantLock lock = new ReentrantLock();
 
 public void run()
 {

//这里加了几次锁,在后面就的要相应的解锁 几次
     
lock.lock();  // block until condition holds
      try {      
       count++;
       System.out.println(" count = "+count);
       try {
    Thread.sleep(3000);
   } catch (InterruptedException e) {
    e.printStackTrace();
   }
       System.out.println(" count = "+count);
      } finally {
      
lock.unlock();
      }
 }
 /**
  * @param args
  */
 public static void main(String[] args) {
  // TODO Auto-generated method stub
  SynchroTest st1 = new SynchroTest();
//  SynchroTest st2 = new SynchroTest();
//  SynchroTest st3 = new SynchroTest();
  

//这里不能够调用   new Thread(st1).run();方法,否则就不是多线程的了
  new Thread(st1).start();
  new Thread(st1).start();
  new Thread(st1).start();
 }

}

如果该线程等待某暂时获取不到的资源,那么我们可以用Condition Object来避免死锁情况。
sufficientFunds = lock .newCondition();
如果条件不满足:
sufficientFunds.await();
这时线程就会释放锁并进入blocked状态,其他线程就有机会执行操作。当其他线程执行完后,就可通知等待的线程继续执行它的操作了:
sufficientFunds.signalAll();


posted @ 2007-05-28 15:39 change| 编辑 收藏

//解决二次提交问题(提交前)

          preSubmitValid(servletRequest,servletResponse);
          
//解决二次提交问题(提交)
    if(!submitValid(servletRequest,servletResponse))
     try
              {
               servletResponse.sendRedirect("public/repeatdeal.jsp");
               return null;
              }
              catch (Exception error)
              {
               servletRequest.setAttribute("errorMSG", "重复提交造成页面跳转出错:" + error.getMessage());
              }   
             
/**
         * 解决二次提交问题(提交前)

         * @param request
         * @param response
         */
        public void preSubmitValid(HttpServletRequest servletRequest,HttpServletResponse response)
        {
         counter = -1;
            servletRequest.getSession().setAttribute("submissioncount",
                    new Integer(counter));
            /**
             * 重要:

             * 通过调用 saveToken(request)方法,动态生成一个token,并且存放到session中,
             * 以便在以后可以在动态生成的页面中加入隐藏字段 <input type="hidden" name="org.apache.struts.taglib.html.TOKEN" value="动态值">
             * 只要调用了该方法,此后包含<html:form...>标签的页面中都会动态生成上面所说的隐藏字段。

             */
            this.saveToken(servletRequest);         
        }
        /**
         * 提交验证,检验是否是重复提交,如果重复提交跳转到统一处理页面
         * @param servletRequest
         * @param servletResponse
         * @return
         */
        public boolean submitValid(HttpServletRequest servletRequest,HttpServletResponse servletResponse)
        {
         counter += 1;
         servletRequest.getSession().setAttribute("submissioncount",new Integer(counter));
            if (!this.isTokenValid(servletRequest))
             return false;

            /**
             * 在认可了用户的合法提交后,一定要调用resetToken(request)重置token,这样session中就没有相应的token啦

             * 这样才能够保证用户再次提交相应数据时,能够检测出来。

             */
            this.resetToken(servletRequest);
            return true;         
        }                      

posted @ 2007-05-28 15:38 change| 编辑 收藏

以前用得的是hibernate3.0.5的版本~~~~而且关于queryfactory 的配置如下:

<prop key="hibernate.query.factory_class">org.hibernate.hql.classic.ClassicQueryTranslatorFactory</prop>

因为数据量超出了10万条~~~~结果出现了 heap 溢出问题,想想了,也确实该溢出了,呵呵~~~

这种查询方式,是将所有的查询出来的结果以对象的形式进行缓存,如此巨大的数据,不把她给称爆炸才怪呢:)

查查hibernate 的文档,有关于大数据量的处理~~~,流程大抵如下:

Transaction tx = session.beginTransaction();

        String hqlDelete = "delete VbufferGis ";
        int deletedEntities = session.createQuery( hqlDelete ).executeUpdate();
        tx.commit();
        session.close();

测试运行出现如下异常:query must begin with SELECT or FROM ,文档是清清楚楚是这样写的嘛,怎么会出现这样的问题呢,是不是和我刚开始的时候一样觉得挺纳闷的,呵呵~~~原来是配置的问题,将上面的配置改为:

<prop key="hibernate.query.factory_class">org.hibernate.hql.ast.ASTQueryTranslatorFactory</prop>

本以为这下是万事大吉了,呵呵~~~~问题有出来了,define class not foundexception :antlr.antlrexception

在网上授了一把,原来hibernate用她来解析 hql ,而我用myEclipse的时候,有没有导入那个包,自然有问题了,

于是将那个包导入,测试删除运行,一切ok!这下是真的万事大吉了吗?还没有,这也难怪我的多磨难了,呵呵

原来在进行待汉字的参数查询的时候出现了乱码现象,感觉挺奇怪的,百思不得其解,幸好有网络这个好东西,google了一下,^_^ 原来值需要换一个版本就ok了,呵呵~~~于是在取sourceforge上面取下了 hibernate-3.1rc2.zip,这下子就ok了!一切运行正常!!!虽然问题是解决了,原理缺不甚明白,有待学习。。。。

posted @ 2007-05-28 15:37 change| 编辑 收藏

在ApplicationContext.xml 里面的配置:


<!-- begin day -->

<bean id="Initlogdatatarget" class="com.sotrip.statistic.scheduling.Initlogdata">
 <property name="tlogvisitDAO"><ref local="tlogvisitDAO"/></property>
 </bean>
<bean id="Jobfortimerdaysservice"
     class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
 <property name="transactionManager"><ref local="transactionManager"/></property>
 <property name="target"><ref local="Initlogdatatarget"/></property>
 <property name="transactionAttributes">
  <props>  
   <prop key="exec*">PROPAGATION_REQUIRED</prop>
  </props>
 </property>
</bean>

<bean id="methodInvokingJobDetail"
 class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> <!--**** 此处的 targetObject 所指定的bean Jobfortimerdaysservice 需是service层的,通过它定义的事务属性 就可以使得 targetMethod 所定义的方法具有事务属性。-->
 <property name="targetObject"><ref bean="Jobfortimerdaysservice"/></property>
 <property name="targetMethod"><value>execute</value></property>
</bean>

<bean id="cronTrigger"
 class="org.springframework.scheduling.quartz.CronTriggerBean">
 <property name="jobDetail">
  <ref bean="methodInvokingJobDetail"/>
 </property>
 <property name="cronExpression">
  <value>0 0/2 * * * ?</value>
 </property>
</bean>

<!-- end day-->

<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
 <property name="triggers">
  <list>
<!--  <ref local="cronTriggertest"/>-->
   <ref local="cronTrigger"/>
    <ref local="cronTriggermonth"/><!-- 此处可以配置多个trigger-->
  </list>
 </property>
</bean>

在就是定时时间的定义:

Cron-Expressions are used to configure instances of CronTrigger. Cron-Expressions are strings that are actually made up of seven sub-expressions, that describe individual details of the schedule. These sub-expression are separated with white-space, and represent:

  1. Seconds
  2. Minutes
  3. Hours
  4. Day-of-Month
  5. Month
  6. Day-of-Week
  7. Year (optional field)

An example of a complete cron-expression is the string "0 0 12 ? * WED" - which means "every Wednesday at 12:00 pm".

cronExpression配置说明

字段   允许值   允许的特殊字符
  0-59   , - * /
  0-59   , - * /
小时   0-23   , - * /
日期   1-31   , - * ? / L W C
月份   1-12 或者 JAN-DEC   , - * /
星期   1-7 或者 SUN-SAT   , - * ? / L C #
年(可选)   留空, 1970-2099   , - * /

 


posted @ 2007-05-28 15:37 change| 编辑 收藏

在spring里面我们一般是这样来使用模板模式的:

JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.update("UPDATE user SET age = 10 WHERE id = 'erica'");

或者:

JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate
.update(
"UPDATE user SET age = ? WHERE id = ?",
new PreparedStatementSetter() {
public void setValues(PreparedStatementSetter ps)
throws SQLException {
ps.setInt(1, 18);
ps.setString(2, "erica");
}
}
);

那么具体在spring里面他是怎么运作的呢?

下面以query查询为例:

public class JdbcTemplate extends JdbcAccessor implements JdbcOperations, InitializingBean {

。。。。。。。。。。。。。。。。。。。。。。。。。

protected Object query(
   PreparedStatementCreator psc, final PreparedStatementSetter pss, final ResultSetExtractor rse)
   throws DataAccessException {
  if (logger.isDebugEnabled()) {
   String sql = getSql(psc);
   logger.debug("Executing SQL query" + (sql != null ? " [" + sql  + "]" : ""));
  }
  return execute(psc, new PreparedStatementCallback() {

//此处以 PreparedStatementCallback 为参数调用 execute()方法,在execute()方法里面回调传入的方法。在回调方法里面即根据传入的 PreparedStatement 执行 查询操作,返回结果。而 PreparedStatement  的获取是在调用回调方法的客户端实现即在execute()方法里面获取,并作为参数传给回调方法。
   public Object doInPreparedStatement(PreparedStatement ps) throws SQLException {
    ResultSet rs = null;
    try {
     if (pss != null) {
      pss.setValues(ps);
     }
     if (getFetchSize() > 0) {
      ps.setFetchSize(getFetchSize());
     }
     rs = ps.executeQuery();
     ResultSet rsToUse = rs;
     if (nativeJdbcExtractor != null) {
      rsToUse = nativeJdbcExtractor.getNativeResultSet(rs);
     }
     return rse.extractData(rsToUse);
    }
    finally {
     JdbcUtils.closeResultSet(rs);
     if (pss instanceof ParameterDisposer) {
      ((ParameterDisposer) pss).cleanupParameters();
     }
    }
   }
  });
 }

那么在execue()方法里面是怎样回调的呢?下面看看execue()方法:

public Object execute(PreparedStatementCreator psc, PreparedStatementCallback action) {
  Connection con = DataSourceUtils.getConnection(getDataSource());
  PreparedStatement ps = null;
  try {
   Connection conToUse = con;
   if (this.nativeJdbcExtractor != null &&
     this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativePreparedStatements()) {
    conToUse = this.nativeJdbcExtractor.getNativeConnection(con);
   }
   ps = psc.createPreparedStatement(conToUse);
   DataSourceUtils.applyTransactionTimeout(ps, getDataSource());
   PreparedStatement psToUse = ps;
   if (this.nativeJdbcExtractor != null) {
    psToUse = this.nativeJdbcExtractor.getNativePreparedStatement(ps);
   }
   Object result = action.doInPreparedStatement(psToUse);
   SQLWarning warning = ps.getWarnings();
   throwExceptionOnWarningIfNotIgnoringWarnings(warning);
   return result;
  }
  catch (SQLException ex) {
   throw getExceptionTranslator().translate(
     "executing PreparedStatementCallback [" + psc + "]", getSql(psc), ex);
  }
  finally {
   if (psc instanceof ParameterDisposer) {
    ((ParameterDisposer) psc).cleanupParameters();
   }
   JdbcUtils.closeStatement(ps);
   DataSourceUtils.closeConnectionIfNecessary(con, getDataSource());
  }
 }

添加删除的操作类似,只是他们的实现都在以execute命名的方法里面。

public void execute(final String sql) throws DataAccessException {
  if (logger.isDebugEnabled()) {
   logger.debug("Executing SQL statement [" + sql + "]");
  }
  class ExecuteStatementCallback implements StatementCallback, SqlProvider {
   public Object doInStatement(Statement stmt) throws SQLException {
    stmt.execute(sql);
    return null;
   }
   public String getSql() {
    return sql;
   }
  }
  execute(new ExecuteStatementCallback());
 }

public Object execute(final StatementCallback action) {
  Connection con = DataSourceUtils.getConnection(getDataSource());
  Statement stmt = null;
  try {
   Connection conToUse = con;
   if (this.nativeJdbcExtractor != null &&
     this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativeStatements()) {
    conToUse = this.nativeJdbcExtractor.getNativeConnection(con);
   }
   stmt = conToUse.createStatement();
   DataSourceUtils.applyTransactionTimeout(stmt, getDataSource());
   Statement stmtToUse = stmt;
   if (this.nativeJdbcExtractor != null) {
    stmtToUse = this.nativeJdbcExtractor.getNativeStatement(stmt);
   }
   Object result = action.doInStatement(stmtToUse);
   SQLWarning warning = stmt.getWarnings();
   throwExceptionOnWarningIfNotIgnoringWarnings(warning);
   return result;
  }
  catch (SQLException ex) {
   throw getExceptionTranslator().translate("executing StatementCallback", getSql(action), ex);
  }
  finally {

//这里就是我们自己写程序的时候需要写的关于数据库链接的关闭操作
   JdbcUtils.closeStatement(stmt);
   DataSourceUtils.closeConnectionIfNecessary(con, getDataSource());
  }
 }

posted @ 2007-05-28 15:34 change| 编辑 收藏

Spring 的声明式事务是通过TransactionProxyFactoryBean 来实现的,而它是通过持有一个拦截器:TransactionInterceptor 来做到的。

public class TransactionProxyFactoryBean extends ProxyConfig implements FactoryBean, InitializingBean {

 private final TransactionInterceptor transactionInterceptor = new TransactionInterceptor();

 /**
  * Set the transaction manager. This will perform actual
  * transaction management: This class is just a way of invoking it.
  * @see TransactionInterceptor#setTransactionManager
  */
 public void setTransactionManager(PlatformTransactionManager transactionManager) {
  this.transactionInterceptor.setTransactionManager( transactionManager);
 }

。。。。。。。。。。//声明的事务属性在这里得到                                   

                                       //见(DefaultTransactionDefinition)定义

}

//TransactionInterceptor 在service层的方法调用的时候,会更具配置判断调用的方法//是否需要事务的处理,若需要则获取设置事务属性对象和事务管理器并启动一个//事务,而其具体的实现是委托给 TransactionAspectSupport 类的//createTransactionIfNecessary 方法实现的,其类结构如下

public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor {

 public Object invoke(MethodInvocation invocation) throws Throwable {
  // Work out the target class: may be null.
  // The TransactionAttributeSource should be passed the target class
  // as well as the method, which may be from an interface
  Class targetClass = (invocation.getThis() != null) ? invocation.getThis().getClass() : null;
  
  // Create transaction if necessary
  TransactionInfo txInfo = createTransactionIfNecessary(invocation.getMethod(), targetClass);//此处即是根据配置的声明性事务属性决定方法事务级别

  Object retVal = null;
  try {
   // This is an around advice.
   // Invoke the next interceptor in the chain.
   // This will normally result in a target object being invoked.
   retVal = invocation.proceed();
  }
  catch (Throwable ex) {
   // target invocation exception
   doCloseTransactionAfterThrowing(txInfo, ex);
   throw ex;
  }
  finally {
   doFinally(txInfo);
  }
  doCommitTransactionAfterReturning(txInfo);

  return retVal;
 }
 
}

//在TransactionAspectSupport 类方法createTransactionIfNecessary()里面根据配置的声明性事务属性,决定启动一个事务,和返回事务级别(信息):

protected TransactionInfo createTransactionIfNecessary(Method method, Class targetClass) {
  // If the transaction attribute is null, the method is non-transactional
  TransactionAttribute transAtt = this.transactionAttributeSource.getTransactionAttribute(method, targetClass);
  TransactionInfo txInfo = new TransactionInfo(transAtt, method);
  if (transAtt != null) {
   // We need a transaction for this method
   if (logger.isDebugEnabled()) {
    logger.debug("Getting transaction for " + txInfo.joinpointIdentification());
   }

   // The transaction manager will flag an error if an incompatible tx already exists

   txInfo.newTransactionStatus(this.transactionManager.getTransaction(transAtt));

//此处的参数 this.transactionManager.getTransaction(transAtt) 即是调用各具体平台的 transactionManager 来获取她的事务属性,在获取事务属性的同时她会更具具体的事务属性 来决定是否开始和怎么开始一个事务;见类AbstractPlatformTransactionManager  结构。

 public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager, Serializable {

。。。。。。。。。。。。。。。。。。。。。

public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {

if (isExistingTransaction(transaction)) {

//下面即是 更具具体的配置事务属性 来决定事务
   if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER)
{
    throw new IllegalTransactionStateException(
      "Transaction propagation 'never' but existing transaction found");
   }
   if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
    if (debugEnabled) {
     logger.debug("Suspending current transaction");
    }
    Object suspendedResources = suspend(transaction);
    boolean newSynchronization = (this.transactionSynchronization == SYNCHRONIZATION_ALWAYS);
    return newTransactionStatus(
      null, false, newSynchronization, definition.isReadOnly(), debugEnabled, suspendedResources);
   }
   else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
    if (debugEnabled) {
     logger.debug("Creating new transaction, suspending current one");
    }
    Object suspendedResources = suspend(transaction);

//此处的doBegin 方法给更具具体的平台和配置事务属性来启动一个事务
    doBegin(transaction, definition);
    boolean newSynchronization = (this.transactionSynchronization != SYNCHRONIZATION_NEVER);
    return newTransactionStatus(
      transaction, true, newSynchronization, definition.isReadOnly(), debugEnabled, suspendedResources);
   }

。。。。。。。。。。。。。。。。。。。。。

}

  // We always bind the TransactionInfo to the thread, even if
  // we didn't create a new transaction here.
  // This guarantees that the TransactionInfo stack will be
  // managed correctly even if no transaction was created by
  // this aspect.
  txInfo.bindToThread();
  return txInfo;
 }

//下面是事务的提交操作,回滚类似

protected void doCommitTransactionAfterReturning(TransactionInfo txInfo) {
  if (txInfo != null && txInfo.hasTransaction()) {
   if (logger.isDebugEnabled()) {
    logger.debug("Invoking commit for transaction on " + txInfo.joinpointIdentification());
   }

  //这里的transactionManager 就是Spring配置文件里面配置的事务 如:org.springframework.orm.hibernate3.HibernateTransactionManager 。
   this.transactionManager.commit(txInfo.getTransactionStatus());

  }
 }

//

protected void doFinally(TransactionInfo txInfo) {
  if (txInfo != null) {
   txInfo.restoreThreadLocalStatus();
  }
 }
private void restoreThreadLocalStatus() {
   // Use stack to restore old transaction TransactionInfo.
   // Will be null if none was set.
   currentTransactionInfo.set(oldTransactionInfo);
  }

//下面以 HibernateTransactionManager  例,说说事务的开始和提交/回滚

//此dobegin()方法即是开始一个事务
 protected void doBegin(Object transaction, TransactionDefinition definition) {
  HibernateTransactionObject txObject = (HibernateTransactionObject) transaction;

  if (txObject.getSessionHolder() == null) {
   Session session = SessionFactoryUtils.getSession(
     getSessionFactory(), getEntityInterceptor(), getJdbcExceptionTranslator(), false);
   if (logger.isDebugEnabled()) {
    logger.debug("Opened new session [" + session + "] for Hibernate transaction");
   }
   txObject.setSessionHolder(new SessionHolder(session), true);
  }

  txObject.getSessionHolder().setSynchronizedWithTransaction(true);
  Session session = txObject.getSessionHolder().getSession();

  try {
   Connection con = session.connection();
   Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
   txObject.setPreviousIsolationLevel(previousIsolationLevel);

   if (definition.isReadOnly() && txObject.isNewSessionHolder()) {
    // just set to NEVER in case of a new Session for this transaction
    session.setFlushMode(FlushMode.NEVER);
   }

   if (!definition.isReadOnly() && !txObject.isNewSessionHolder()) {
    // we need AUTO or COMMIT for a non-read-only transaction
    FlushMode flushMode = session.getFlushMode();
    if (FlushMode.NEVER.equals(flushMode)) {
     session.setFlushMode(FlushMode.AUTO);
     txObject.getSessionHolder().setPreviousFlushMode(flushMode);
    }
   }

   // add the Hibernate transaction to the session holder

//此处即是真正的调用了hibernate的session开始一个事务session.beginTransaction() 。
   txObject.getSessionHolder().setTransaction(session.beginTransaction());

   // register transaction timeout
   if (definition.getTimeout() != TransactionDefinition.TIMEOUT_DEFAULT) {
    txObject.getSessionHolder().setTimeoutInSeconds(definition.getTimeout());
   }

   // register the Hibernate Session's JDBC Connection for the DataSource, if set
   if (getDataSource() != null) {
    ConnectionHolder conHolder = new ConnectionHolder(con);
    if (definition.getTimeout() != TransactionDefinition.TIMEOUT_DEFAULT) {
     conHolder.setTimeoutInSeconds(definition.getTimeout());
    }
    if (logger.isDebugEnabled()) {
     logger.debug("Exposing Hibernate transaction as JDBC transaction [" +
       conHolder.getConnection() + "]");
    }
    TransactionSynchronizationManager.bindResource(getDataSource(), conHolder);
    txObject.setConnectionHolder(conHolder);
   }

   // bind the session holder to the thread
   if (txObject.isNewSessionHolder()) {
    TransactionSynchronizationManager.bindResource(getSessionFactory(), txObject.getSessionHolder());
   }
  }

  catch (Exception ex) {
   SessionFactoryUtils.closeSessionIfNecessary(session, getSessionFactory());
   throw new CannotCreateTransactionException("Could not create Hibernate transaction", ex);
  }
 }

//回滚

protected void doCommit(DefaultTransactionStatus status) {
  HibernateTransactionObject txObject = (HibernateTransactionObject) status.getTransaction();

。。。。。。。。。。。。。。。。。。。。。。。。。。
  try {
   txObject.getSessionHolder().getTransaction().commit();
  }
  catch (net.sf.hibernate.TransactionException ex) {
   // assumably from commit call to the underlying JDBC connection
   throw new TransactionSystemException("Could not commit Hibernate transaction", ex);
  }
  catch (JDBCException ex) {
   // assumably failed to flush changes to database
   throw convertJdbcAccessException(ex.getSQLException());
  }
  catch (HibernateException ex) {
   // assumably failed to flush changes to database
   throw convertHibernateAccessException(ex);
  }
 }

  }
  else {
   // The TransactionInfo.hasTransaction() method will return
   // false. We created it only to preserve the integrity of
   // the ThreadLocal stack maintained in this class.
   if (logger.isDebugEnabled())
    logger.debug("Don't need to create transaction for " + methodIdentification(method) +
      ": this method isn't transactional");
  }

posted @ 2007-05-28 15:33 change| 编辑 收藏

引用:http://shidu.blogbus.com/logs/2004/12/540365.html

配置文件大概如下:
<action-mappings>
<action path="/account"
name="accountForm"
parameter="action"
scope="request"
type="com.ai.uaap.admin.web.action.AccountAction">
<forward name="listOK" path="/AccountMaster.jsp" />
<forward name="removeOK" path="/account.do?action=list" redirect="true" />
</action>

我在执行完remove的方法之后的代码是return mapping.findForward("removeOK")。这时就会访问account.do?action=list这个地址,以前我想在account.do?action=list之后继续添加参数(例如account.do?action=list&abc=123)不知道该怎么实现。

今天看到一个资料给出了实现:
String path = mapping.findForward("removeOK").getPath();
ActionForward forward = new ActionForward(path + "&abc=123", true);
//这里的true是Redirect
return forward;

posted @ 2007-05-28 15:32 change| 编辑 收藏

CREATE TABLE V_GIS_MONTH
(
  ID         VARCHAR2(255 BYTE)                 NOT NULL,
  VISITTIME  DATE                               DEFAULT NULL,
  GISNAME    VARCHAR2(255 BYTE),
  GISLEVEL   VARCHAR2(255 BYTE),
  PARENT     VARCHAR2(255 BYTE),
  VNUMBER    NUMBER(19),
  IPFROM     VARCHAR2(255 BYTE),
  IPLEVEL    VARCHAR2(2 BYTE),
  GISPATH    VARCHAR2(1024 BYTE)
)
TABLESPACE USERS

PCTUSED    0
PCTFREE    10
INITRANS   1
MAXTRANS   255
STORAGE    (
            INITIAL          64K
            MINEXTENTS       1
            MAXEXTENTS       2147483645
            PCTINCREASE      0
            BUFFER_POOL      DEFAULT
           )
LOGGING
NOCACHE
NOPARALLEL;


ALTER TABLE V_GIS_MONTH ADD (
  PRIMARY KEY (ID)
    USING INDEX
    TABLESPACE USERS
    PCTFREE    10
    INITRANS   2
    MAXTRANS   255
    STORAGE    (
                INITIAL          64K
                MINEXTENTS       1
                MAXEXTENTS       2147483645
                PCTINCREASE      0
               ));


select gisdays.ipfrom,gisdays.gisname,sum(gisdays.vnumber)AS count from V_Gis_Month gisdays where gisdays.GISPATH = '/sotrip_dest/destination_home/china/zhejiang/' and gisdays.iplevel='3' group by gisdays.ipfrom,gisdays.gisname ORDER BY count desc;

gis:表示目的地

在数据库里面这样的查询是可以的,但是用这样的语句在hibernate的DAO层

这样做:

String queryString = "select gisdays.ipfrom,sum(gisdays.vnumber) AS count  from "+strtable+" gisdays where gisdays.iplevel='"+striplevel+"' "+strquery+" and to_char(gisdays.visittime,'YYYY-MM-DD')  between '"+strfrom+"' and '"+strto+"' group by gisdays.ipfrom ORDER BY count desc";           
  

Iterator iterator = this.getHibernateTemplate().find(queryString).iterator();

是不行的,会出现异常,报错。

改为:

String queryString = "select gisdays.ipfrom,sum(gisdays.vnumber)  from "+strtable+" gisdays where gisdays.iplevel='"+striplevel+"' "+strquery+" and to_char(gisdays.visittime,'YYYY-MM-DD')  between '"+strfrom+"' and '"+strto+"' group by gisdays.ipfrom ORDER BY sum(gisdays.vnumber) desc";           

 Iterator iterator = this.getHibernateTemplate().find(queryString).iterator();

while(iterator.hasNext())
  {
   Object[] pair = (Object[])iterator.next();   
   VGisMonth gismonth = new VGisMonth();
   for(int i=0;i<pair.length;i++)
   {
    System.out.println("pair["+i+"]="+pair[i]);
   }

}//这里iterator 所返回的是对象数组,它把取得的每一列都作为一个对象,然后返回一个对象数组的叠代器,然后我们就可以取出里面的每一列(对象)转换成相应的数据类型,(这里 strtable 是table所对应的实体的名字)

就可以了。应该是在里面不支持对  统计添加的列 的别名命名吧。呵呵:)

posted @ 2007-05-28 15:31 change| 编辑 收藏

      本命年已经悄然过去,在这一年里面确实有太多的心酸泪史。虽然没有招受太多肉体上的伤害,但是生心的痛苦远胜于此。

       过去的日子总感觉自己活的浑浑噩噩的。生活,学习,感情,都是一团糟。然而在这段晕呼的岁月里面却有一件事情总是不间断,那就是思索生活的意义。”活着是为了什么“,“。。。”诸如此类的莫名其妙的为什么还有很多很多,我想我都快可以自己在写一本十万个为什么了。说句老实话,本人没有为了人类的从高理想而活的高尚情操,所以这种思索只会让我变得更加的颓废,萎缩。我想不清楚这么高深的问题。也许是太富有哲理性了,更本就不是我这种凡夫俗子所能够想的通的。对于这种问题的思索让我失去了方向,干什么事情都找不着北。也因此带来了很多的困扰,也因此而承受了很多原本就不应该承受的负担。太多的思绪,太多的顾虑,太多的想不通,太多,太多,太多的问题使我的脑袋都已经在塞不下任何其他的东西。

      在这一年里面,我们去了索创的培训,这确实是一个不错的机会,然而心不在焉的也没有把心思花在上面,一连串的心事让我身在教室心在外。虽是基本不间断的去上课,可我能够感觉的到,我像是一具形尸走肉,盲目的穿行在纷乱的人群里面。感觉自己真的使那么的孤单,那么的无助。慢慢的自己也就开始喜欢上了幻想,喜欢活在自己的梦里面。

       在后来就进了公司去实习,这也许就是一个战时的避难所,然而它并没有使我得到多少解脱,我还是依然活在我的梦里面。我希望通过不间歇的劳作使自己尽量的不要理会那些身外之事,然而我毕竟还没有去少林出家,自然少不了七情六欲,固然没有炼得真身,依然为尘世所绕。也许活在自己的梦里面是幸福的,梦真的醒来也是幸福的,但是我却处在了阴阳交界似睡非省的边缘。糊里糊涂的活着,活在稀里糊涂的世界里面。。。

       有人说失败的男人才会想家。看来我真的是一个失败的男人,因为今年的我就特别的想回家。也许我真的是需要在家里调养一下,忘却我心中的那份不应有的牵挂。

posted @ 2007-05-28 15:31 change| 编辑 收藏

相信大家对于mvc模式并不陌生,它的主要作用就是使 :结果展示,业务操作,以及后台的数据库操作,

起到隔离的效果,这种分层解偶 的思想最大的好处应该就是便于扩展吧。对于现在比较流行的网站建设架构

structs+hibernate+spring来说,就是用struct的标签来做结果的展示,用它的action来处理业务逻辑,而用

hibernate来处理业务逻辑所要操作的后台数据库数据。而他们各自的扩展变化和具体操作实现的变化 基本上

都不合影响到其他层的改动。个人感觉   数据库的设计定了,那么更具业务需求的hibernate接口成基本上就是

定下来了,因为数据库的操作无非不是根据 不同的条件实现  增,删,改,查!说穿也就是说hibernate所做的

只是对于你所设计的表 做 增,删,改,查 操作,而这些操作基本上是固定模式。对于spring的用处主要就是

用它的课配置事务处理的能力。

posted @ 2007-05-28 15:29 change| 编辑 收藏

最近用structs+hibernate+spring做的一个项目,后台用的是oracle92。

测试的时候发现了一个数据的插入问题,在数据库里面定义的一个varchar(4000)的字段,在action层打印显示的字段大小明明就是<4000字节的  可是一执行用hibernate插入操作的时候,就报错,而且显示的字段大小只要一超过2000就报错说超出范围,而且汉字还是3字节编码的,而所有的页面都用gbk编码郭了,一直百思不得其解,原来是oracle驱动得问题。oracle92自己带的驱动只是适合jdk1.3以前的,在oracle的官方网站上下载一个最新驱动,一切ok!晕吧!呵呵,希望大家  不要范和我一样的毛病。为此二郁闷。

posted @ 2007-05-28 15:28 change 阅读(130) | 评论 (0)编辑 收藏

比如在删除一条在数据库操作的时候 我们一般是类似是这样使用:

this.getHibernateTemplate().delete("from Information where INFOID='"+infoid.trim()+"'");

然而具体在spring内部是怎么操作的呢?

delete()----->excecute()----->执行回调方法HibernateCallback .doInHibernate()。

下面来让我们来直接看一下spring的源代码。

//hibernate回调接口

public interface HibernateCallback {

Object doInHibernate(Session session) throws HibernateException, SQLException;

}

//

package org.springframework.orm.hibernate;

public class HibernateTemplate extends HibernateAccessor implements HibernateOperations {

//。。。。。。。。。。。。。。。。

public int delete(final String queryString) throws DataAccessException {
  Integer deleteCount = (Integer) execute(new HibernateCallback() {//定义回调实现
   public Object doInHibernate(Session session) throws HibernateException {
    checkWriteOperationAllowed(session);
    return new Integer(session.delete(queryString));//此处有hibernate的实现操作
   }
  });
  return deleteCount.intValue();
 }

 public Object execute(HibernateCallback action) throws DataAccessException {
  Session session = (!isAllowCreate() ? SessionFactoryUtils.getSession(getSessionFactory(), false) :
  SessionFactoryUtils.getSession(getSessionFactory(), getEntityInterceptor(), getJdbcExceptionTranslator()));
  boolean existingTransaction = TransactionSynchronizationManager.hasResource(getSessionFactory());
  if (!existingTransaction && getFlushMode() == FLUSH_NEVER) {
   session.setFlushMode(FlushMode.NEVER);
  }
  try {
   Object result = action.doInHibernate(session);//此处调用hibernatecallback回调接口即hibernate的实现
   flushIfNecessary(session, existingTransaction);
   return result;
  }
  catch (HibernateException ex) {
   throw convertHibernateAccessException(ex);
  }
  catch (SQLException ex) {
   throw convertJdbcAccessException(ex);
  }
  catch (RuntimeException ex) {
   // callback code threw application exception
   throw ex;
  }
  finally {
   SessionFactoryUtils.closeSessionIfNecessary(session, getSessionFactory());
  }
 }

//。。。。。。。。。。。。。。

//其他操作类似

}


posted @ 2007-05-28 15:27 change| 编辑 收藏