2008 六月 04 , 11:02下午
in category 原创空间 by BruceJini
java 串行化有两个比较普遍的接口,一个是Serialable,一个Externalizable,前者直接恢复二进制数据,后者会对对象重组。具体细节可以看看下面的代码<BR><BR>
package serial;
import java.io.Externalizable;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
public class TestExternalizable implements Externalizable{
String name;
public TestExternalizable() {
System.out.println("1");
// TODO Auto-generated constructor stub
}
public TestExternalizable(String name) {
System.out.println("2");
// TODO Auto-generated constructor stub
System.out.println("name:" + this.name);
this.name = name;
System.out.println("name:" + this.name);
}
public static void main(String[] args) {
TestExternalizable test = new TestExternalizable("brucejini");
FileOutputStream out;
try {
out = new FileOutputStream("d:/dd.sys");
ObjectOutputStream oos = new ObjectOutputStream(out);
oos.writeObject(test);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
FileInputStream in;
try {
in = new FileInputStream("d:/dd.sys");
ObjectInputStream ois = new ObjectInputStream(in);
TestExternalizable testin = (TestExternalizable) ois.readObject();
// TestExternalizable test = new TestExternalizable("");
// test.readExternal(ois);
System.out.println(testin.name);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
// TODO Auto-generated method stub
this.name = (String)in.readObject();
// System.out.println(test.name);
}
public void writeExternal(ObjectOutput out) throws IOException {
// TODO Auto-generated method stub
out.writeObject(name);
}
<CODE><XMP>
运行后的结果:
2
name:null
name:brucejini
1
brucejini
可以看出,实际上在尝试反串行化之时,对象是被重新装配起来啊的,基本上可以看到这个过程。自定义串行化,当然,如果属性对象存在的话,也会在父对象之前执行,也还可以去试试
Permalink
[]
2007 六月 13 , 01:28下午
in category 原创空间 by BruceJini
见 http://www.squirrelsql.org/#installation 。java开发中的数据库都支持,驱动管理,JDBC中的所有API返回信息浏览,等等。就是SQL 的语法提示会与输入法冲突,好像还不能改。
Permalink
[]
2007 五月 30 , 02:57下午
in category Java摘录 by BruceJini
Permalink
[]
2007 五月 15 , 05:37下午
in category 原创空间 by BruceJini
上一个程序其实是以一种“轮询”来控制线程调度的,这样不是个好办法,主线程轮询占用了不少资源,即使存在时间间隔。小程序还行,程序大了,线程执行时间和线程数量越多,消耗就越大。
相对来说,使用“事件”驱动就是个比较好的选择了。再看看下面用事件实现同样的调度:
ThreadManager.java
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* 该类用来管理所有线程,map和在所有线程和此类对象之间是需要同步的.
* 所有方法之间需要按照一定的流程执行
*
*/
public class ThreadManager {
private Thread[] li;
private Map map;
private ThreadGroup gup;
private Map users;
private static String threadname = Constants.THREAD_NAME;//线程名常量
/**
* @param x
* 创建的线程个数
*/
public ThreadManager(int x) {
// 初始化线程列表
li = new Thread[x];
map = Collections.synchronizedMap(new HashMap(x));
gup = new ThreadGroup("register"); //线程组名
users = new HashMap();
// 创造n个注册处理线程
for (int i = 0; i < x; i++) {
li[i] = new RegisterThread(gup, threadname + String.valueOf(i), map);
}
}
/**
* @return
* @throws InterruptedException
*/
public Map first() throws InterruptedException {
for (int i = 0; i < li.length; i++) {
RegisterThread th = (RegisterThread) li[i];
th.start();
}
//主线程在完成启动所有子线程后阻塞
synchronized (this) {
this.wait();
//主线程被唤醒后并不马上执行,等待最后一个注册线程完成
if (isAllThreadWaiting()) {
Thread.sleep(10);
}
}
return map; //返回中途的结果集
}
/**
* 线程再度执行的方法
* @param result
* @return
* @throws InterruptedException
*/
public Map second(Map result) throws InterruptedException {
map.putAll(result); //再度执行的线程可能需要新的待处理数据,这里假设还是同一个map
for (int i = 0; i < li .length; i++) {
//唤醒所有等待中的线程
synchronized (li [i]) {
li [i].notifyAll();
}
}
//同样的,主线程停止等待返回结果
synchronized (this) {
this.wait();
}
return map; //假设继续执行的线成还是处理数据map
}
/**
* 判断线程队列中是否仅有当前线程未进入等待状态
*
* @param me
* @return
*/
public boolean isAllOtherThreadWaiting(Thread me) {
for (int i = 0; i < threadlist.length; i++) {
if (threadlist[i] != me
&& threadlist[i].getState().equals(Thread.State.WAITING) != true) {
return false;
}
}
return true;
}
/**
* 判断线程队列中是否所有线程进入等待状态
*
* @param me
* @return
*/
public boolean isAllThreadWaiting() {
for (int i = 0; i < threadlist.length; i++) {
if (threadlist[i].getState().equals(Thread.State.WAITING) != true) {
return false;
}
}
return true;
}
/**
* 判断当前manager所在线程是否waiting
*
* @return
*/
public boolean isManagerThreadWaiting() {
if (Thread.currentThread().getState().equals(Thread.State.WAITING)) {
return true;
}
return false;
}
}
RegisterThread.java
import java.io.IOException;
import java.util.Map;
public class RegisterThread extends Thread {
private Map map;
private ThreadManager manager;
public RegisterThread(ThreadGroup gup,String name ) {
super(gup,name);
}
public RegisterThread(ThreadGroup gup,String name,Map map,ThreadManager manager){
this(gup,name);
this.map = map;
this.manager = manager;
}
public void run() {
//do something with map here
map.put(Thread.currentThread().getName(), "数据");
//暂停等待用户输入验证值并重新唤醒本线程
if (manager.isAllOtherThreadWaiting(currentThread())) {
synchronized (manager) {
manager.notify();
}
}
//然后让注册线程阻塞
synchronized (this) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//do something here again
//递交表单
map.put(Thread.currentThread().getName(),"新数据");
if (Thread.activeCount() == 1 ) {
synchronized (manager) {
manager.notify();
}
}
}
}
手写的代码比较乱。总的来说这个原理就是由主线程启动所有子线程,主线程然后等待,好比老板叫手下干活,布置好任务,然后自己去睡觉。每个最后一个子线程如果发现自己是最后一个,就把主线程唤醒,好比最后一个干活还没趴下的手下在昏倒前把老板唤醒了,注意,这时最后一个手下是不能倒的,不然就没人叫醒老板了,全倒了你的程序就永远长眠了。老板醒了,等到最后一个手下也躺了,他一次把所有人都叫醒,并给他们新任务,注意要等最后一个也要倒了才叫,不然就不是都叫醒了,嘿嘿。老板又睡了,这时候所有的手下都陆续累死了,最后一个快死的把老板叫醒了,老板得到他们的劳动成果,找用户领赏钱去了。上面的过程基本就是这样。
Permalink
[]
2007 五月 11 , 09:53上午
in category 心情故事 by BruceJini
先看一个简单的例子,主线程控制所有的子线程调度:
ThreadManager.java
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* 该类用来管理所有线程,map和在所有线程和此类对象之间是需要同步的.
* 所有方法之间需要按照一定的流程执行
* @author hwx
*
*/
public class ThreadManager {
/**
* Logger for this class
*/
private Thread[] li;
private Map map;
private ThreadGroup gup;
private Map users;
private static String threadname = Constants.THREAD_NAME;//线程名常量
/**
* @param x
* 创建的线程个数
*/
public ThreadManager(int x) {
// 初始化线程列表
li = new Thread[x];
map = Collections.synchronizedMap(new HashMap(x));
gup = new ThreadGroup("register"); //线程组名
users = new HashMap();
// 创造n个注册处理线程
for (int i = 0; i < x; i++) {
li[i] = new RegisterThread(gup, threadname + String.valueOf(i), map);
}
}
/**
* 发送请求并返回包含所有图片的数据
* @return
* @throws InterruptedException
*/
public Map first() throws InterruptedException {
for (int i = 0; i < li.length; i++) {
RegisterThread th = (RegisterThread) li[i];
th.start();
}
// 等待所有线程完成部分进度,并且将结果写入同步的map中,下面的代码通过验证结果集来判断是否所有的线程都已经完成一定的进度
while (map.size() < li.length) {
Thread.currentThread().sleep(100);
}
return map; //返回中途的结果集
}
/**
* 线程再度执行的方法
* @param result
* @return
* @throws InterruptedException
*/
public Map second(Map result) throws InterruptedException {
map.putAll(result); //再度执行的线程可能需要新的待处理数据,这里假设还是同一个map
for (int i = 0; i < li.length; i++) {
RegisterThread th = (RegisterThread) li[i];
th.resume(); //重新启动线程
}
while (gup.activeCount() > 0) { //这通过判断线程组来决定是否所有线程都终结
Thread.currentThread().sleep(1000);
}
return map; //假设继续执行的线成还是处理数据map
}
}
RegisterThread.java
import java.io.IOException;
import java.util.Map;
public class RegisterThread extends Thread {
private Map map;
public RegisterThread(ThreadGroup gup,String name ) {
super(gup,name);
}
public RegisterThread(ThreadGroup gup,String name,Map map){
this(gup,name);
setMap(map);
}
public void run() {
//do something here
map.put(Thread.currentThread().getName(), "数据");
//暂停等待用户输入验证值并重新唤醒本线程
this.suspend();
//do something here again
//递交表单
map.put(Thread.currentThread().getName(),"新数据");
this.stop();
}
public Map getMap() {
return map;
}
public void setMap(Map map) {
this.map = map;
}
}
代码的数据仅仅为一个同步Map类,调度判断使用的是"轮询"方式,轮询数据和线程组,当然还有更加好的处理方式。这种情况在web开发中比较常见,比如你必须要等待n个线程都完成对数据的处理才统一给客户端一个web页面。
Permalink
[]
2007 五月 08 , 03:55下午
in category 原创空间 by BruceJini
今天遇到一个异常java.lang.ClassCastException: [Ljava.lang.String; cannot be cast to java.lang.Strings.原因是调用了servlet中HttpServletRequest.getParameterMap().
Map paras = arg0.getParameterMap();
后来才发现这个返回的Map中的键值对为
,并且此Map同步锁别占用,不能修改其已存在数据,否则抛出异常:java.lang.IllegalStateException: No modifications are allowed to a locked ParameterMap
如果你需要动态的得到所有post参数,还需要一个另外的Map来putall.当然也可以在调用的时候取其数组的[0],不过容易发生误会。
Permalink
[]
2007 五月 01 , 09:06上午
in category 原创空间 by BruceJini
Thread.stop()已经被宣告为过时,因为它突然的杀死线程会释放所有的对象锁。这可能出现不可预料的错误,所以我们需要谨慎使用,相传有一种安全的取代方法:
public class AlternateStop extends Object implements Runnable {
private volatile boolean stopRequested;
private Thread runThread;
public void run() {
runThread = Thread.currentThread();
stopRequested = false;
int count = 0;
while (!stopRequested) {
System.out.println("Running ... count=" + count);
count++;
try {
Thread.sleep(300);
} catch (InterruptedException x) {
Thread.currentThread().interrupt(); // reassert
}
}
}
public void stopRequest() {
stopRequested = true;
if (runThread != null) {
runThread.interrupt();
}
}
public static void main(String[] args) {
AlternateStop as = new AlternateStop();
Thread t = new Thread(as);
t.start();
try {
Thread.sleep(2000);
} catch (InterruptedException x) {
// ignore
}
as.stopRequest();
}
}
代码摘自《java Thread programming》,其设定了一个标示变量来控制线程,并且使用sleep()和interrupt()来停止正在执行中的线程。但是这到底和stop()有什么区别呢?假如两者都在同步块中执行,一个会悄悄的释放同步锁,一个会抛出异常,但到底怎么来处理,实在是没遇到过这种实际情况.......
Permalink
[]
2007 四月 22 , 01:17上午
in category 原创空间 by BruceJini
java API提供了一个计时器框架,简单的例子是这样的:
public class EggTimer {
private final Timer timer = new Timer();
private final int minutes;
public EggTimer(int minutes) {
this.minutes = minutes;
}
public void start() {
timer.schedule(new TimerTask() {
public void run() {
playSound();
timer.cancel();
}
private void playSound() {
System.out.println("Your egg is ready!");
// Start a new thread to play a sound...
}
}, minutes * 10 * 1000);
}
public static void main(String[] args) {
EggTimer eggTimer = new EggTimer(2);
eggTimer.start();
}
}
以上代码来自IBM的网站,一个煮蛋定时器,就是煮两分钟蛋然后plausound.可以看出来这个框架的结构:主线程也就是Timer所在的线程负责计时,等到时间达到条件,启动新的线程即Task线程。当然这是比较简单的例子,复杂的情况还要另想办法。
现在我假如不用它这个,What,s the plan?。想达到按时调用的情况大概有两种思路(可能还有其它),一种就是所谓的“轮询”,负责控制时间的线程不断的判断自己是否满足执行条件,利用时间变量来做,整个运行的过程中程序不断的做判断,直到过了一段时间,条件达到了。显然这是一种很直观但是很傻的方法。另外一种就是利用线程的等待或休眠来做,这样cpu是不要做那些判断的,停多少时间的事情因该交给了更底层的实现,这个我也不清楚,可能是CPU直接控制的。在JDK没有计时器框架以前可能就是这样做的。我自己做了个,每隔一定时间执行任务:
class Timer extends Thread{
int refreshtime;
Object target;
public Timer(int refreshTime,Object target) {
this.refreshtime = refreshTime; //间隔时间
this.target = target; //调度任务所在的对象
}
@Override
public void run() {
System.out.println("now start time recoding....... ");
try {
sleep(1000); //等待一小段时间以保证主线程进入阻塞状态
while(true){
synchronized (target) {
target.notify(); //等待中的线程开始运行,下一轮计时期也同时开始
}
sleep(refreshtime); //间隔休眠
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
这个类可以这样调用:
Thread timerecoder = new Timer(refreshtime,this);
timerecoder.start(); //启动计时线程
while(true){
synchronized (this) {
this.wait(); //阻塞当前线程
// do something here
}
}
任务所在的线程启动了计时线程,这个和计时器框架相反,他的因该好点,这里就不改了。大概就是任务线程阻塞,然后时间条件满足就被唤醒。中间需要注意对象锁的争用,在线程sleep的时候它会保持对象锁的状态,所以不能把它放到同步块中,如果不释放同步锁而休眠,你的任务线程也会等待,因为同步锁没有交给它,这就影响任务的执行时间,完全不对了。
总的来说,要自己实现计时调度还是比较复杂的,因为涉及到同步问题,稍不留神就错了。上面的sleep就是个例子,可以把它放到上面的同步块中,时间就合单线程的效果一样了。
Permalink
[]
2007 四月 10 , 09:45下午
in category 原创空间 by BruceJini
如下
<%@page import="java.io.PrintWriter"%>
<%@page import="java.util.Enumeration"%>
<%@ page language="java" contentType="text/html; charset=utf8" pageEncoding="utf8"%>
<%
//页面调试
Enumeration appnames = application.getAttributeNames();
Enumeration sesnames = session.getAttributeNames();
Enumeration reqnames = request.getAttributeNames();
%>
web context
############################ application #######################################
<%
while (appnames.hasMoreElements()) {
String name = (String) appnames.nextElement();
%>
<%= name %>: <%= application.getAttribute(name) %>
<%}%>
############################ session #######################################
<%
while (sesnames.hasMoreElements()) {
String name = (String) sesnames.nextElement();
%>
<%= name %>: <%= session.getAttribute(name) %>
<%}%>
############################ request #######################################
<%
while (reqnames.hasMoreElements()) {
String name = (String) reqnames.nextElement();
%>
<%= name %>: <%= request.getAttribute(name) %>
<%}%>
可以include到每个页面的公共部份,如footer的最下面就挺好,可以在开发中监视每个作用域。
Permalink
[]
2007 四月 02 , 08:24下午
in category 原创空间 by BruceJini
今天做了EcodeFilter来解决乱码问题,可是却一直不起作用,最后终于发现问题所在,估计很多人也遇到了。首先来看一个EcodeFilter的实现,很简单的:
public void doFilter(ServletRequest arg0, ServletResponse arg1,
FilterChain arg2) throws IOException, ServletException {
//arg0.getParameter("name");
arg0.setCharacterEncoding("utf-8");
//String name= new String(arg0.getParameter("name").getBytes("utf-8"),"utf-8");
//System.out.println(name);
arg2.doFilter(arg0, arg1);
}
这样配置的EcodeFilter是起作用的,把第一部分注释去掉就能看到效果。但这时我们把第一部分注释也去掉会出现完全相反的结果了!?不信的朋友可以试试,出现乱码了。原因很简单,第一次从request中getParameter()的时候,所有的参数都会初始化,这里包括“name”,当然还有其它的。初始化的编码默认为“iso8859_1”,如果初始化之前你没有设置编码就不会起作用的,这点很重要,所以
EcodeFilter最好放在最前面,保证是在初始化参数以前。不然,嘿嘿,你的乱码问题是解决不了的哦。
Permalink
[]
2007 四月 02 , 12:29下午
in category Java摘录 by BruceJini
标签库必须坚持“不从自定义标签中生成HTML代码”的设计抉择,而应该使自定义标签把动态值填充到HTML的模板中。不管你怎么设计,记住HTML编辑者是无法看见你自动生成的HTML代码的。
很多JSP标签库都是遵循这一原则,如JSTL,反面的例子就是.net了,他的很多控件(标签库)实践程度不高就是在其中嵌入了HTML代码,也许,它(微软)总是认为HTML开发人员会喜欢他自动添加的HTML标签,如"<td>....</td>",可是他没想过如果他们不想要这个HTML标签了,他们不得不为此更换控件(标签库).............
Permalink
2007 三月 25 , 12:00上午
in category Java摘录 by BruceJini
一个事务属性可能有下面的属性之一:
☆ Required
☆ RequiresNew
☆ Mandatory
☆ NotSupported
☆ Supports
☆ Never
Required
如果客户端正在一个运行的事务中调用一个企业Bean的方法,这个方法就在这个客户端的事务中执行。如果客户端不关联一个事务,这个容器在运行该方法前开始一个新的事务。
Required属性在许多事务环境中可以很好的工作,因此你可以把它作为一个默认值,至少可以在早期开发中使用。因为事务的属性是在部署描述符中声明的,在以后的任何时候修改它们都很容易。
RequiresNew
如果客户端在一个运行的事务中调用企业Bean的方法,容器的步骤是:
1.挂起客户端的事务
2.开始一个新的事务
3.代理方法的调用
4.方法完成后重新开始客户端的事务
如果客户端不关联一个事务,容器运行这个方法以前同样开始一个新的事务。如果你想保证该方法在任何时候都在一个新事物中运行,使用RequiresNew属性。
Mandatory
如果客户端在一个运行的事务中调用企业Bean的方法,这个方法就在客户端的事务中执行。如果客户端不关联事务,容器就抛出TransactionRequiredException 异常。
如果企业Bean的方法必须使用客户端的事务,那么就使用Mandatory属性。
NotSupported
如果客户端在一个运行的事务中调用企业Bean的方法,这个容器在调用该方法以前挂起客户端事务。方法执行完后,容器重新开始客户端的事务。
如果客户端不关联事务,容器在方法运行以前不会开始一个新的事务。为不需要事务的方法使用NotSupported属性。因为事务包括整个过程,这个属性可以提高性能。
Supports
如果客户端在一个运行的事务中调用企业Bean的方法,这个方法在客户端的事务中执行,如果这个客户端不关联一个事务,容器运行该方法前也不会开始一个新的事务。因为该属性使方法的事务行为不确定,你应该谨慎使用Supports属性。
Never
如果客户端在一个运行的事务中调用企业Bean的方法,容器将抛出RemoteException异常。如果这个客户端不关联一个事务,容器运行该方法以前不会开始一个新的事务。
Permalink
2007 三月 22 , 12:17上午
in category 心情故事 by BruceJini
欧罗巴是木星的第六颗已知卫星,是17颗木星卫星中体积较大的一颗,据科学推测,它是太阳系中有可能存在生命的卫星之一。[
Read More]
Permalink
2007 三月 17 , 11:06下午
in category 心情故事 by BruceJini
Spring确实不错,用起来简单,单例模式什么的都能自己实现,不用再花时间做重复工作。很多东西用起来也简单,但是感觉文档太少了,网上的文章都是些入门的,论坛总是解决不了我的迫切需要,refrences只做了些主要的说明,Rod的那本书还算有些内容,不过很大部分是在说他的经验。,按照spring的思想,每一种方案都会给你几种实现的选择,细节部分的疑问都只有自己试。全盘的学习Spring其实就是在了解j2ee的基本架构,J2EE到底需要做到什么的疑问也会慢慢得到解答,加上Rod的那本without ejb实在是入门的好方法。
Permalink
2007 三月 14 , 11:08上午
in category 原创空间 by BruceJini
在web.xml中声明
context
org.springframework.web.context.ContextLoaderServlet
1
contextConfigLocation
/WEB-INF/beans.xml
即可以加载Spring的context,那么如何取得这个context呢?遍历ServletContext有以下结果
org.apache.catalina.jsp_classpath
javax.servlet.context.tempdir
org.springframework.web.context.WebApplicationContext.ROOT
org.apache.catalina.resources
org.springframework.web.servlet.FrameworkServlet.CONTEXT.Dispatcher
org.apache.catalina.WELCOME_FILES
其中“org.springframework.web.context.WebApplicationContext.ROOT”就是context的全局变量名。当然,有个更简单的办法,就是用Spring的工具类WebApplicationContextUtils
WebApplicationContextUtils.getWebApplicationContext(ServletContext sc)
Permalink