
		 
		2009年4月24日		  
	
 
	
			
			此时程序中最多有几个存活的线程,个人认为应该是4个,怎么就是看不到是4个的结果呢?
package Test;
public class Testthread{
     public static void main(String[] args){
      //测定当前活动的线程数目 肯定有一个主线程在运行
      System.out.println("=========header========"+Thread.activeCount());
      ThreadTest tt=new ThreadTest();  
      RunTest rt=new RunTest(); //在下面新建线程里被启动
      new Thread(rt).start();      //新建立的一个线程 启动
      tt.start();                        //已经建立的线程 启动
      System.out.println("===========footer======"+Thread.activeCount());
 }
}
class ThreadTest extends Thread{
 public void run(){
  System.out.println("==========I'm over thread=========");
 }
}
class RunTest implements Runnable{
 public void run(){
  System.out.println("==========I'm over runnable==========");
 }
}
打印结果:
第一类结果 (次序有时不同)
=========header========1
===========footer======3
==========I'm over thread=========
==========I'm over runnable==========
第二类结果
=========header========1
==========I'm over runnable==========
===========footer======2
==========I'm over thread=========
			posted @ 
2009-04-24 15:04 王业平 阅读(1178) | 
评论 (6) | 
编辑 收藏 
		
			
		
			
			Servlet的生命周期:
(1)装载Servlet。这项操作一般是动态执行的。然而,Server通常会提供一个管理的选项,用于在Server启动时强制装载和初始化特定的Servlet;
(2)Server创建一个Servlet的实例;
(3)Server调用Servlet的init()方法;
(4)一个客户端的请求到达Server;
(5)Server创建一个请求对象;
(6)Server创建一个响应对象;
(7)Server激活Servlet的service()方法,传递请求和响应对象作为参数;
(8)service()方法获得关于请求对象的信息,处理请求,访问其他资源,获得需要的信息;
(10)service()方法使用响应对象的方法,将响应传回Server、最终到达客户端。
service()方法可能激活其它方法以处理请求,如doGet()或doPost()或程序员自己开发的新的方法;
对于更多的客户端请求,Server创建新的请求和响应对象,仍然激活此Servlet的service()方法,将这两个对象作为参数传递给它。如此重复以上的循环,但无需再次调用init()方法。一般Servlet只初始化一次;
当Server不再需要Servlet时,Server调用Servlet的Destroy()方法。
			posted @ 
2009-04-24 10:13 王业平 阅读(1234) | 
评论 (2) | 
编辑 收藏 
		
 
	
		
	
		
		 
		2009年4月23日		  
	
 
	
			
			不知如何转帖,只有ctrl +v了
Servlet线程安全探讨
    
        
            
              
            
              
            我们在开发JAVA WEB应用程序,大多都会考虑用MVC模式的框架来部署(相信没有程序员再考虑前两者简单的模式了吧。),这也是基于下面的原因: 
            一、MVC由于分层清晰,容易看到整个系统流程的架构,对于越来越复杂的系统是有相当大的帮助。 
            二、 扩展性,耦合性低。各层影响相当少,如JSP页面只负责数据显示,M负责业务逻辑处理,C相当于Servlet来控制流程的转向。等等这一系列的好处。 
            用MVC的模式,其本质就是用Servlet的应用技术。Servlet/jsp和其他如ASP\PHP语言相比,由于使用了多线程运行技术与具有很高的执行效率。但是也就是Servlet由于默认多线程模式执行,依我们所了解线程安全性问题,也就不得不要考虑在Servlet中也存在这样的问题,然而,很多程序员只专注于业务逻辑的处理,并没有注意到多线程的安全性的问题(在此编写之前,我也存在这样的经历,不过还好。。。。),这往往造成编写的程序在用户量少的时候没出什么问题,而一旦发现大量的并发用户时,而且这数量达到一定的数量时,就会出现一系列莫名的问题,这问题在下面我们可以看的到。 
            Servlet的多线程机制是怎么样的呢: 
            Servlet体系结构是建立在JAVA的多线程机制上的,但它的生命周期是由WEB容器来管理的,当客户端第一次请求某个Servlet时,Servlet容器会根据web.xml的配置实例化相应的Servlet类,当有新的客户端来请求这个Servlet时,容器一般不会再实例化这个Servlet类,而是以线程方式去调用这个实例的方法,然后再有更多的客户端来请求时,就存在了多个线程在使用这个实例。并且Servlet容器会自动使用线程池技术来支持系统的运行。 
            在这样的情况,当两个或者多个客户端同时请求同一Serlvet时,就会存在多个线程同时访问同一资源的情况,数据就可能变的不一致,所以在用Servlet搭建WEB应用程序时如果不考虑线程的问题,就会出现难以发现的问题。 
            Servlet线程安全问题的例子: 
            Servlet线程是由于使用实例变量不当而导致的,这里有如下的例子: 
            代码程序如下: 
            public class SecurityTest extends HttpServlet { 
                PrintWriter output;//成员变量 
             
                @Override 
                protected void service(HttpServletRequest request, 
                       HttpServletResponse response) throws ServletException,  
            IOException { 
                   response.setContentType("text/html;charset=gb2312"); 
                   String name = request.getParameter("name"); 
                   output=response.getWriter(); 
                   try { 
                       Thread.sleep(5000); 
                   } catch (InterruptedException e) { 
                       e.printStackTrace(); 
                   } 
                   output.write(name); 
                } 
             
            } 
            该实例中定义了一个实例变量output,在service方法中负责输出用户名,当一个用户访问该Servlet时,程序会正常的运行,但当多个用户并发的访问时,就可能会出现其他的用户信息显示在另一个用户的浏览器上的问题,为了看到实际的效果,在这个程序中,我特做如下的处理:就是延时5000毫秒,让第一个用户暂停在输出数据前。然后我们马上发起另一个请求,这种情况下就会出现如下的页面 
            第一次请求:http://127.0.0.1:8080/Test2/securityTest?name=a 
             
            大家看到这页面什么数据也没有,就是说那姓名没有打印出来,那跑到哪里去了呢?看第二个用户请求的情况。 
            第二个用户请求地址:http://127.0.0.1:8080/Test2/securityTest?name=b 
             
            可以看到,原来a值已经打印到第二个用户的浏览器了。 
            可以想像在暂停5000时间里,第二个用户请求这个servlet,已经把output的引用变成了第二个用户请求的output值了,这样就解释了为什么会输出到第二个客户端的浏览的原因。 
             
            从内存模型来看Servlet的线程安全问题//不是很理解这一段 
               JAVA的内存模型JMM(JAVA Memory Model)主要是为了规定线程与内存的一些关系,既然Servlet也是用线程技术,那么我们也从这方面寻找根本原因,根据JMM,系统存在有主内存,JAVA的实例变量(就是类变量吧)都是存在于主内存供其他内存使用,也就是对所有的线程都是共享的,而根据线程的特点:它是有自己的工作内存的,工作内存包括缓存和堆栈两部分,堆栈是专门用来存在方法中的局部变量的,而缓存则是主内存中变量的拷贝,缓存与主内存并不总是同步的,也就是缓存中的变量的修改可能没有立刻写到主存中,如下图: 
             
            根据内存模型,我们可以得到如下的线程调度表: 
            
            调度时刻
            a线程
            b线程
            T1
            访问Servlet页面
            
            T2 
            
            访问Servlet页面
            T3 
            output=a的输出username=a休眠5000毫秒,让出CPU 
            
            T4 
            
            output=b的输出(写回主存)username=b休眠5000毫秒,让出CPU
            T5 
            在用户b的浏览器上输出a线程的username的值,a线程终止。
            
            T6
            
            在用户b的浏览器上输出b线程的username的值,b线程终止。 
            可以看出,由于b线程对实例变量output的修改覆盖了a线程对实例变量output的值,直接导致了用户a的信息显示到b的浏览器上。根据内存模型,我们也可以解释到,正是因为b开始时修改了缓存中的output值,然后刷新到主内存中,而又有足够的时间刷新到a缓存中,这时a的Output值就直接导致了指向b浏览器。 
            解决方法: 
            从上面的分析中,我们知道导致线程不安全的主要原因在于实例变量的使用不当,下面就提出如下三种解决方法 
            第一,让Servlet类实现SingleThreadModel接口,该接口指定系统如何处理对同一个Servlet的调用,如果一个Servlet被指定实现这个接口,那么,在这个Servlet中的service方法将不会在两个线程中同时执行,也就是说执行完一个后再执行下一个请求的service,当然也就不存在线程不安全的问题了。 
            代码如下: 
            public class SecurityTest extends HttpServlet implements SingleThreadModel{} 
            其实这方法也就相当于是同步方法的效果吧。 
             
            第二.同步对共享数据的操作。我们所熟悉的就是用synchronized关键字,这样能保证一次只有一个线程来操作被保护的区段。在本例子也可以用synchronized来保证线程的安全,代码如下: 
            public class SecurityTest extends HttpServlet { 
                PrintWriter output; 
             
                @Override 
                protected void service(HttpServletRequest request, 
                       HttpServletResponse response) throws ServletException, IOException { 
                   response.setContentType("text/html;charset=gb2312"); 
                   String name = request.getParameter("name"); 
            Synchronized(this){ 
                   output=response.getWriter(); 
                   try { 
                       Thread.sleep(5000); 
                   } catch (InterruptedException e) { 
                       e.printStackTrace(); 
                   } 
                   output.write(name); 
            } 
                }} 
            第三:避免使用实例变量,而使用局部变量。因为Servlet线程不安全的原因是由实例变量引起,所以我们可以避免使用实例变量,而使用局部变量,线程之间很难直接访问局部变量 ,这样就从根本上解决了这一问题。 
            在本例子中,就是将output放在service方法中当局部变量 。 
            public class SecurityTest extends HttpServlet { 
             
                @Override 
                protected void service(HttpServletRequest request, 
                       HttpServletResponse response) throws ServletException, IOException { 
                    PrintWriter output; 
                   response.setContentType("text/html;charset=gb2312"); 
                   String name = request.getParameter("name"); 
                   output=response.getWriter(); 
                   try { 
                       Thread.sleep(5000); 
                   } catch (InterruptedException e) { 
                       e.printStackTrace(); 
                   } 
                   output.write(name); 
                }} 
            这三种方法都对解决servlet线程安全起到很好的作用,但我们如果对他们进行比较一下,看哪一种更适合呢: 
            第一个方案中:实现SingleThreadModel接口,Servlet引擎将为每个客户请求都生成一个Servlet实例,这将引起大量的系统开销,在新版本的Servlet2.4中也不提倡使用了。 
            第二个方案中:在程序中使用同步来保护要使用的共享数据,也使系统的性能大大的下降,这是因为被同步的代码在同一时刻只能由一个线程来执行,使得同时处理其他客户请求的吞吐量大大降低,大量客户处于阻塞状态,这对于并发用户请求来说并非是一件很好的事情。另外为了保持主内存与工作内存数据的一致性要频繁地刷新缓存,这也大大降低了系统性能,所以这种方案不大可取。 
            第三个方案则应该是最优方案:从JAVA内存模型来看,方法中的临时变量都是在栈中分配空间,而每个线程都有自己的私有栈空间,互不干扰,不会影响性能,也不会产生线程安全的问题。 
             
             | 
        
    
 
			posted @ 
2009-04-23 16:31 王业平 阅读(409) | 
评论 (0) | 
编辑 收藏 
		
			
		
			
			现在要求输入一个文件的目录,之后将里面所有的备份文件删除,备份文件都是以“.bak”或".BAK"结尾
package TestFile;
import java.io.File;
import java.io.FileFilter;
/**
 * @author 王业平
 * 用于过滤以.bak结尾或包含.bak的文件
 * 返回值的含义是 当包含该字符串时返回true
 */
public class ListFilter implements FileFilter{
 @Override
 public boolean accept(File file) {
    //测试指定的文件(夹)是否应该包含在指定的列表中(就是测定作为过滤的条件是否满足)
  /*
   * 如果是目录的话,直接返回true 表示是满足条件的一情况
   */
  if(file.isDirectory()) return true;
  /*
   * 如果不是目录的话,通过判断在其名字里是否含有规定的字符,
   * 因为string的index方法在不满足条件(不含有指定的字符)是返回-1
   * 所以以此来确定是否满足条件
   */
  String name=file.getName();
    /*
     * int index=name.indexOf(".bak"); 
     * return index!=-1;
  * 这种方法不安全,可能会删除文件名中还含有.bak的文件,备份文件还可以用.BAK结尾
    */
    return name.endsWith(".bak")||name.endsWith(".BAK");
 }
}
使用此类
package TestFile;
import java.io.File;
/** 
 * @author Administrator
 * 涉及到递归调用
 */
public class BakDelete{
 public static void main(String[] args){
  BakDelete bd=new BakDelete();
  bd.listBakFile(new File("E:\\"));
 }
 public void listBakFile(File file){
  File[] fs=file.listFiles(new ListFilter());
  for(int i=0;i<fs.length;i++){
   if(fs[i].isFile()){
    System.out.println(fs[i].getAbsolutePath());//打印文件绝对路径
    fs[i].delete();
   }else{
    //System.out.println(fs[i].getAbsolutePath());
    listBakFile(fs[i]);
   }    
  }
  //System.out.println(fs.length);
 }
}
			posted @ 
2009-04-23 13:54 王业平 阅读(1255) | 
评论 (7) | 
编辑 收藏 
		
 
	
				
 
	
		
			
	我是菜鸟,所以我很谦虚,很谨慎,可是还是很多的bug,
我不断的努力,纵然很多困难,可我依然谦虚谨慎,因为那就是我坚持,淡定的看着水云,望着辽远的天空... 
			
	 | 
|
| 26 | 27 | 28 | 29 | 30 | 31 | 1 | 
| 2 | 3 | 4 | 5 | 6 | 7 | 8 | 
| 9 | 10 | 11 | 12 | 13 | 14 | 15 | 
| 16 | 17 | 18 | 19 | 20 | 21 | 22 | 
| 23 | 24 | 25 | 26 | 27 | 28 | 29 | 
| 30 | 1 | 2 | 3 | 4 | 5 | 6 | 
			
常用链接
留言簿(2)
		随笔档案
		
				
			
	
搜索
最新评论
	
阅读排行榜
评论排行榜