快捷搜索:

Java中finalize()的另类用法

做过JAVA编程的都知道,在JAVA中有一种垃圾网络器的机制,当它运行时(平日在系统内存低到必然限度时自动运行),会收受接收不再应用的工具所占用的内存,以是,在JAVA法度榜样中,我们平日只斟酌创建工具,而从不关心工具的清除。Finalize()是JAVA为类供给的一种特殊措施。垃圾网络器的事情历程大年夜致是这样的:一旦垃圾网络器筹备好开释无用工具占用的存储空间,它首先调用那些工具的finalize()措施,然后才真正收受接收工具的内存。经由过程应用finalize(),就可以在垃圾网络器运行时代进行一些特殊的事情。下面一例就阐清楚明了finalize()的一种奇妙用法。

现在的商业利用系统越来越多的采纳WEB形式。在WEB形式利用中,每一次页面造访是自力的,前后不相关联,哪怕多个用户在同一时候造访利用的同一个页面,用户互相之间也是不知道的。假如想要反省当前有哪些用户正在应用系统(如筹备规复数据备份或进行系统进级时,系统治理员都很盼望知道这些信息),该怎么办呢? 基于Servlet、Jsp技巧的WEB办事器供给了隐含的Session、Application工具,使用它可以帮开拓者实现一些信息的持续保存和共享。当用户造访一个WEB利用时,WEB办事器会自动创建一个Session工具,该工具可以供用户在会话期内在利用的所有页面中共享数据; Application是WEB利用的一个全局工具。使用Session、Application工具,可以达到跟踪所有用户信息的目的。

当用户打开浏览器开始哀求WEB利用的登录页面时,WEB办事即为该客户创建一个session,此后,在session的timeout光阴内,该客户都应用这个session(timeout光阴可设置,如Tomcat办事器是在各利用的web.xml文件中设置)。假如应用IE浏览器,Session与客户IP地址、客户法度榜样进程ID所合营标识的连接有对应关系,相同IP地址、相同进程的窗口(如经由过程IE-文件-新建-窗口 打开的新窗口)具有同一个session,以是session可用于标识各个自力的客户利用连接。

下面是一个样例

为了方便处置惩罚,先建一个简单类(user)用来表达用户信息及寄放sessionId:

package com;

public class user {

public String name="";

public String sessionId="";

}

另一个类(testSession)用于处置惩罚用户的login、logout等动作信息,使系统可以跟踪当前连接的用户信息。

package com;

import java.util.Vector;

import com.user;

public class testSession {

public user User;

private Vector vsid;

public testSession()

{

User=new user();

}

public boolean verify(String username,String password)

throws Exception //验证用户/密码

{

return true;

}

public void setSessionVar(String sesid,Vector sid) {

this.User.sessionId=sesid;

this.vsid=sid;

}

private static synchronized void addappses(user puser,

Vector pvsid) { //记录一个新连接的用户

int pos=-1;

user l_user;

if (puser==null || pvsid==null)

return;

for(int i=0;i<pvsid.size();i++){

l_user=(user)pvsid.get(i);

if(l_user.sessionId.equals(puser.sessionId)){

pos=i;

break;

}

}

if(pos==-1){

pvsid.add(puser);

}

else{

pvsid.set(pos,puser);

}

}

private static synchronized void removeappses(user puser,

Vector pvsid) { //移除一个退出的用户

int pos=-1;

user l_user;

if (puser==null || pvsid==null)

return;

for(int i=0;i<pvsid.size();i++){

l_user=(user)pvsid.get(i);

if(l_user.sessionId.equals(puser.sessionId)){

pos=i;

break;

}

}

if(pos!=-1){

pvsid.remove(pos);

}

}

protected void finalize() {

this.removeappses(this.User,this.vsid);

}

public boolean login(String username) throws Exception

{ //处置惩罚登录

this.User.name=username;

this.addappses(this.User,this.vsid);

return true;

}

public boolean logout() throws Exception

{ //处置惩罚注销

this. finalize();

this.User=null;

this.vsid=null;

return true;

}

}

每一个用户均建立一个testSession工具,来保存该用户的信息。为了对类testSession进行阐明,必须同时惹人另一个文件logintest.jsp。这个用于示例的JSP文件供给一个简单的界面进行登录、注销处置惩罚。文件内容如下:

<%@ page import=" com.testSession,

java.util.Vector"%>

<%@page contentType="text/html;charset=GBK" %>

<% request.setCharacterEncoding(response.

getCharacterEncoding());%>

<%

String actionType=request.getParameter("actiontype");

String actionResult="";

if(actionType!=null) {

if(actionType.equals("login")){ // -1-

String userName=request.getParameter("username");

if(userName==null || userName.equals("")){

;

}

else{

String password=request.getParameter("password");

if(password==null)

password="";

testSession ts=

(testSession)session.getAttribute("testSession");

if(ts!=null) { //-1.1-

session.removeAttribute("testSession");

if( !ts.User.name.equals(""))

ts.logout();

}

ts=new testSession();

if(!ts.verify(userName,password)) {

//验证用户与密码,看是否合法用户

actionResult="login fail";

//不法用户,显示差错信息

}

else{ //验证成功

session.setAttribute("testSession",ts);

Vector app_vts=

(Vector)application.getAttribute("app_vts");

if(app_vts==null) {

app_vts=new Vector();

application.setAttribute("app_vts",app_vts);

}

ts.setSessionVar(session.getId(),app_vts);

ts.login(userName);

actionResult=userName+" login success";

}

}

}

if(actionType.equals("logout")){

testSession ts=

(testSession)session.getAttribute("testSession");

if(ts!=null) {

session.removeAttribute("testSession");

if( !ts.User.name.equals("")){ //-2-

actionResult=ts.User.name;

ts.logout();

}

session.invalidate();

}

actionResult=actionResult+" logout success";

}

}

else

actionResult="null";

%>

<head>

<script LANGUAGE="Javascript"></script>

<script>

function doAction(actionType)

{

document.test.actiontype.value=actionType;

document.test.submit();

}

</script>

</head>

<body>

<table width="80%" border="1" align="center" >

<form method="POST" action="logintest.jsp"

name="test">

<tr>

<td height="33" align="right">用户:</td>

<td width="70%"> <input name="username"

type="text" value="" size="20">

</td>

</tr>

<tr>

<td width="27%" height="22" align="right">密码:</td>

<td width="73%">

<input name="password" type="password" size="20">

</td>

</tr>

<tr>

<td height="32" colspan="2" align="right">

<div align="center">

<input name="B1" type="button" value="登录"

onclick="doAction(´login´)">

<input name="B2" type="reset"

value="重写">

<input name="B3" type="button" value="注销"

onclick="doAction(´logout´)">

</div></td>

</tr>

<tr>

<td width="27%" height="22" align="right">

<input name="actiontype" type="hidden"

value=""></td>

<td width="73%"> <input name="info" type="text"

size="20" value="<%=actionResult%>">

</td>

</tr>

</form>

</table>

</body>

[-1-]:法度榜样的if(actionType.equals("login")){…}部分处置惩罚login。[-1.1-]前后部分先经由过程session.getAttribute("testSession");取得session中保存的会话变量ts(一个testSession工具实例)。假如ts为空,表示当前用户还没有login,否则用户已经login了,则先logout再从新login,并将新testSession工具保存到session里。application.getAttribute("app_vts");所取得的变量app_vts中保存了所有当前登任命户的user信息。每个用户login成功时,即往app_vts中添加一个user工具,这是经由过程testSession的addappses措施完成的。而当用户注销时,先从session.getAttribute("testSession")中取到当前用户的testSession工具,该工具已含有application.getAttribute("app_vts")工具的引用,经由过程testSession的logout措施进行注销处置惩罚(见[-2-]标记前后)。TestSession.logout终极是经由过程调用removeappses措施从全局工具app_vts中移除用户信息的。总结来说,法度榜样使用利用全局工具application来保存跟踪用户连接信息的工具(例中为app_vts),该工具记录着利用中所有用户的连接、退出信息。

JSP页面运行的界面如图:

screen.width-333)this.width=screen.width-333;">

当我们输入用户testuser_1并按<登录>按钮,按钮下面的文本框显示"testuser_1 login success"。我们使用viewSessiones.jsp来察当作果,显示如下:

testuser_1 sessionId=A16DCE950C2C664D0AA93E05B27D8E00

viewSessiones.jsp文件的内容如下:

<%@ page import="com.testSession, com.user,

java.util.Vector"%>

<%@ page contentType="text/html; charset=GBK"%>

<% request.setCharacterEncoding(response.

getCharacterEncoding()); %>

<%

Vector l_vts=(Vector)application.getAttribute("app_vts");

user l_us;

if(l_vts!=null){

for(int i=0;i<l_vts.size();i++){

l_us=(user)l_vts.get(i);

out.println(l_us.name+" sessionId="+l_us.sessionId);

out.println("<br>");

}

}

%>

viewSessiones.jsp文件的感化是将app_vts中的用户信息显示出来。

当我们从桌面再启动一个IE法度榜样,输入用户testuser_2并按<登录>按钮,按钮下面的文本框显示"testuser_2 login success"。我们使用viewSessiones.jsp来察当作果,显示如下:

testuser_1 sessionId=A16DCE950C2C664D0AA93E05B27D8E00

testuser_2 sessionId=34B0AF3F1F2573F1C1DD12D62DF06F91

而当我们在第一个IE中按下按钮<注销>,logintest.jsp的显示为:

刷新viewSessiones.jsp来察当作果,显示如下:

testuser_2 sessionId=BC487C6A9FD663EA27E797A420B41051

我们在第二个IE中按下按钮<注销>,按钮下面的文本框显示"testuser_2 login success", 刷新viewSessiones.jsp来察当作果,显示出已经没有连接的用户信息。

上面演示中,用户信息的移除是经由过程调用类的logout()措施来实现的。但要是用户没有点按<注销>按钮而直接关闭IE或转到其他网站,该用户信息不是就不停存留在系统中吗?

让我们看看类testSession中的一个措施:

protected void finalize() {

this.removeappses(this.User,this.vsid);

}

用户造访利用,只有一个进口:login。利用的所有用户登录都可以被察看到。用户脱离利用,有三种可能:注销、转到其他网站、直接关闭浏览器。选择注销脱离利用,可以被法度榜样察看到(logout),而后两种要领的脱离利用,却不会调用logout。要察看到后两种要领,就必要应用工具的finalize()措施。

用户经由过程转到其他网站、直接关闭浏览器两种要领脱离利用跨越Session的timeout光阴时,用户的Session工具会自动掉效,即变为无用工具,而列入了垃圾网络器的收受接收范围;关联的,"寄存"在Session中的testSession工具会同时变为无用工具(在其生命期,仅存在Session对它的引用,Session掉效了,它的独一引用者不存在了,也就变成了无用工具)。垃圾网络器运行时,首先会调用testSession的finalize(),testSession就经由过程在finalize()措施中清除app_vts中存储的本用户信息。在testSession类代码中可看到,finalize()调用类的removeappses()措施履行实际的清除操作。垃圾网络器的运行,除了让其根据必要自动启动外,也可经由过程法度榜样调用来启动它,比如:System.gc() 就直接启动系统垃圾网络动作。

可以想见,本例假如晦气用类的finalize()措施,我们很难找到另一种简便的道路来达到清除用户信息的目的,由于用户非正常脱离利用的事故对WEB办事端来说是无法感知的。

在testSession类代码中还有一个对照特殊的地方,实现用户信息加入和清除的两个措施addappses 和removeappses都被定义为static synchronized 类型。为什么呢?这是同步的必要。

App_vts是个利用级的全局可共享工具,在同一时候连接到WEB 上的有多个用户,这些用户都可以操作对象app_vts。假如有两个或以上的用户同时调用testSession的addappses或removeappses措施(这是完全可能的,由于WEB SERVER是多线程办事),将带来弗成预感的结果。

为了防止两个或以上的客户法度榜样(属于不合线程)同时造访一个资本,Java供给了一种内建的机制来办理冲突。这种机制便是synchronized(同步)。在一个类中将一个特定的措施设为synchronized(同步的),便可有效地防止冲突,在任何时候,只能有一个线程调用特定工具的一个synchronized措施(只管那个线程可以调用多个工具的同步措施),另一个线程只有等上一线程对该措施的调用履行完毕后才能得到该措施的调用权。大年夜致的事情机制可以这样觉得:每个工具都包孕了一把锁(也叫作"监视器"),它自动成为工具的一部分;调用任何synchronized措施时,工具就会被锁定,弗成再调用那个工具的其他任何synchronized措施,除非第一个措施完成了自己的事情,并解除锁定。在类testSession中,将 addappses和removeappses这两个措施设为了synchronized,当调用testSession工具的addappses措施时,便不能再同时调用testSession工具的removeappses措施,反之亦然。

Synchronized又有两个级别。当我们将一个措施仅仅设为synchronized时,那是工具级的"锁",虽然一个工具的synchronized措施弗成同时调用,却可以同时调用不合工具的同一个synchronized措施。以是这样做还没完全办理问题,由于两个用户(各有自己的testSession工具)可以同时调用addappses措施同时操作app_vts。当我们将一个措施设为static synchronized时,则是类级的"锁"。类包孕的"锁"(自动作为类的Class工具的一部分),可在一个类的范围内被互相间锁定起来,从那个类创建的所有工具都共享一把"锁"。就如testSession实际所做的那样,addappses 和removeappses被定义为static synchronized 类型,这样,任一时刻,所有线程用户中肯定只能有一个用户调用addappses 和removeappses两者中的一个措施,达到防止冲突的目的。

以上样例在WINDOWS 2000、TOMCAT40、JDK13中经由过程。

摘自:天极网   光阴:2003年9月10日

您可能还会对下面的文章感兴趣: