随笔-42  评论-42  文章-0  trackbacks-0
用于线程实现的类和接口分别是什么?简单举出使用范例。

JAVA线程的实现--Thread类与Runnable接口

  要记住的一件重要的事情是main() 函数也是一个线程,并可用来做有用的工作。程序员只有在需要多个线程时才需要创建新的线程。
  Thread 类
  Thread 类是一个具体的类,而非抽象类.该类封装了线程的行为。要创建一个线程,必须创建一个从 Thread 类继承的新类。程序员必须覆盖 Thread 的 run() 函数来完成有用的工作。用户并不直接调用此函数;而是必须调用 Thread 的 start() 函数,该函数再调用 run()。下面的代码说明了它的用法:
  创建两个新线程:

import java.util.*;

class TimePrinter extends Thread {
 
int pauseTime;
 String name;
 
public TimePrinter(int x, String n) {
  pauseTime 
= x;
  name 
= n;
 }


 
public void run() {
  
while(true{
   
try {
    System.out.println(name 
+ ":" + new    
           Date());
    Thread.sleep(pauseTime);
   }
 catch(Exception e) {
    System.out.println(e);
   }

  }

 }


 
static public void main(String args[]) {
  TimePrinter tp1 
= new TimePrinter(1000"Fast Guy");
  tp1.start();
  TimePrinter tp2 
= new TimePrinter(3000"Slow Guy");
  tp2.start();

 }

}
 在本例中,我们可以看到一个简单的程序,它按两个不同的时间间隔(1 秒和 3 秒)在屏幕上显示当前时间。这是通过创建两个新线程来完成的,包括 main() 共三个线程。但是,因为有时要作为线程运行的类可能已经是某个类层次的一部分,所以就不能再按这种机制创建线程。虽然在同一个类中可以实现任意数量的接口,但 Java 编程语言只允许一个类有一个父类。同时,某些程序员避免从 Thread 类导出,因为它强加了类层次。对于这种情况,就要 runnable 接口。

  Runnable 接口
  此接口只有一个函数,run(),此函数必须由实现了此接口的类实现。但是,就运行这个类而论,其语义与前一个示例稍有不同。我们可以用 runnable 接口改写前一个示例。
  创建两个新线程而不强加类层次:
import java.util.*;

class TimePrinter implements Runnable {
 
int pauseTime;
 String name;
 
public TimePrinter(int x, String n) {
  pauseTime 
= x;
  name 
= n;
 }


 
public void run() {
  
while(true{
   
try {
    System.out.println(name 
+ ":" + new Date());
    Thread.sleep(pauseTime);
   }
 catch(Exception e) {
    System.out.println(e);
   }

  }

 }


 
static public void main(String args[]) {
  Thread t1 
= new Thread(new TimePrinter(1000"Fast Guy"));
  t1.start();
  Thread t2 
= new Thread(new TimePrinter(3000"Slow Guy"));
  t2.start();
 }

}
 请注意,当使用 runnable 接口时,不能直接创建所需类的对象并运行它;必须从 Thread 类的一个实例内部运行它。许多程序员更喜欢 runnable 接口,因为从 Thread 类继承会强加类层次。

练习:
package thread;

import java.util.Date;

public class ThreadTest2 extends Thread {
    
private String name;
    
private int time;

    
public ThreadTest2(String name, int time) {
        
super();
        
this.name = name;
        
this.time = time;
    }


    @Override
    
public void run() {
        
try {
            Thread.sleep(time);
            System.out.println(name 
+ ":" + new Date());
        }
 catch (Exception e) {
            
// TODO: handle exception
        }

    }


    
public static void main(String[] args) {
        System.out.println(
"chh");
        ThreadTest2 tt2 
= new ThreadTest2("yiqi"5000);
        tt2.start();
    }

}



package thread;

import java.util.Date;

public class RunnableImpl2 implements Runnable {

    
private long time;

    
public RunnableImpl2(long time) {
        
super();
        
this.time = time;
    }


    @Override
    
public void run() {
        
try {
            Thread.sleep(time);
            System.out.println(
"jn" + ":" + new Date());
        }
 catch (Exception e) {
            
// TODO: handle exception
        }

    }


    
public static void main(String[] args) {
        Thread td 
= new Thread(new RunnableImpl2(5000));
        td.start();
        System.out.println(
"haha" + ":" + new Date());

    }

}



  synchronized(同步的) 关键字
 
  到目前为止,我们看到的示例都只是以非常简单的方式来利用线程。只有最小的数据流,而且不会出现两个线程访问同一个对象的情况。但是,在大多数有用的程序中,线程之间通常有信息流。试考虑一个金融应用程序,它有一个 Account 对象,如下例中所示:
  一个银行中的多项活动:

 
public class Account {
 String holderName;
 
float amount;
 
public Account(String name, float amt) {
 holderName 
= name;
 amount 
= amt;
}


public void deposit(float amt) {
 amount 
+= amt;
}


public void withdraw(float amt) {
 amount 
-= amt;
}


public float checkBalance() {
 
return amount;
}

}

 在此代码样例中潜伏着一个错误。如果此类用于单线程应用程序,不会有任何问题。但是,在多线程应用程序的情况中,不同的线程就有可能同时访问同一个 Account 对象,比如说一个联合帐户的所有者在不同的 ATM 上同时进行访问。在这种情况下,存入和支出就可能以这样的方式发生:一个事务被另一个事务覆盖。这种情况将是灾难性的。但是,Java 编程语言提供了一种简单的机制来防止发生这种覆盖。每个对象在运行时都有一个关联的锁。这个锁可通过为方法添加关键字 synchronized 来获得。这样,修订过的 Account 对象(如下所示)将不会遭受像数据损坏这样的错误:
public class Account {
 String holderName;
 
float amount;
 
public Account(String name, float amt) {
 holderName 
= name;
 amount 
= amt;
}


public synchronized void deposit(float amt) {
 amount 
+= amt;
}


public synchronized void withdraw(float amt) {
 amount 
-= amt;
}


public float checkBalance() {
 
return amount;
}

}

  对一个银行中的多项活动进行同步处理:

  deposit() 和 withdraw() 函数都需要这个锁来进行操作,所以当一个函数运行时,另一个函数就被阻塞。请注意, checkBalance() 未作更改,它严格是一个读函数。因为 checkBalance() 未作同步处理,所以任何其他方法都不会阻塞它,它也不会阻塞任何其他方法,不管那些方法是否进行了同步处理.



关于synchronized

1、synchronized关键字的作用域有二种:

1)是某个对象实例内,synchronized aMethod(){}可以防止多个线程同时访问这个对象的synchronized方法(如果一个对象有多个synchronized方法,只要一个线程访问了其中的一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法)。这时,不同的对象实例的synchronized方法是不相干扰的。也就是说,其它线程照样可以同时访问相同类的另一个对象实例中的synchronized方法;

2)是某个类的范围,synchronized static aStaticMethod{}防止多个线程同时访问这个类中的synchronized static 方法。它可以对类的所有对象实例起作用。

2、除了方法前用synchronized关键字,synchronized关键字还可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。用法是: synchronized(this){/*区块*/},它的作用域是当前对象;

3、synchronized关键字是不能继承的,也就是说,基类的方法synchronized f(){} 在继承类中并不自动是synchronized f(){},而是变成了f(){}。继承类需要你显式的指定它的某个方法为synchronized方法;



posted on 2008-06-17 20:55 BlueSunshine 阅读(273) 评论(7)  编辑  收藏 所属分类: 参考资料

评论:
# re: 面试题 2008-06-18 12:23 | BlueSunshine
collection 与 collections 的区别

 Collection是集合的接口(不包括 map), set 和 list 这两个接口继承了 collection 接口;
Collections是针对集合类的一个帮助类,他提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作。
  回复  更多评论
  
# re: 面试题 2008-06-18 12:26 | BlueSunshine
final, finally, finalize的区别

final 用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,类不可继承。
finally是异常处理语句结构(可选的)的一部分,表示总是执行。try{}catch(){}finally{}
finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回收,例如关闭文件等。
  回复  更多评论
  
# re: 面试题 2008-06-18 12:41 | BlueSunshine
HashMap和Hashtable的区别
 
  Hashtable继承自Dictionary类(Dictionary类也实现了Map 接口),而HashMap是Java1.2引进的Map interface的一个实现类。他们都实现了Map接口。Hashtable和HashMap采用的hash/rehash算法都大概一样,所以性能不会有很大的差异。

  区别1:HashMap允许将null作为一个entry(名值对)的key或者value,由于非线程安全,效率上可能高于Hashtable,而Hashtable不允许。HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey。因为contains方法容易让人引起误解。

  区别2:Hashtable的方法是Synchronize的,而HashMap不是,在多个线程访问Hashtable时,不需要自己为它的方法实现同步,而HashMap 就必须为之提供外同步。
  回复  更多评论
  
# re: 面试题 2008-06-18 12:51 | BlueSunshine
session 与cookie 的区别
 
  Session是由应用服务器维持的一个服务器端的存储空间,Cookie是客户端的存储空间,由浏览器来维持。用户在连接服务器时,会由服务器生成一个唯一的SessionID,用该SessionID 为标识符来存取服务器端的Session存储空间。而SessionID这一数据则是保存到客户端,用Cookie保存的,用户提交页面时,会将这一 SessionID提交到服务器端,来存取Session数据。
 
  
  服务器也可以通过URL重写的方式来传递SessionID的值,因此不是完全依赖Cookie。如果客户端Cookie禁用,则服务器可以自动通过重写URL的方式来保存Session的值,并且这个过程对程序员透明。可以试一下,即使不写Cookie,在使用request.getCookies();取出的Cookie数组的长度也是1,而这个Cookie的名字就是JSESSIONID,还有一个很长的二进制的字符串,是SessionID的值。

  回复  更多评论
  
# re: 面试题 2008-06-18 13:06 | BlueSunshine
JVM
 
Java Virtual Machine(Java虚拟机),它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。Java虚拟机有自己完善的硬件架构,如处理器、堆栈、寄存器等,还具有相应的指令系统。JVM屏蔽了与具体操作系统平台相关的信息,使得Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。Java虚拟机在执行字节码时,实际上最终还是把字节码解释成具体平台上的机器指令执行。
  回复  更多评论
  
# re: 面试题 2008-06-18 13:16 | BlueSunshine
Servlet
 
  Servlet是使用Java Servlet 应用程序设计接口(API)及相关类和方法的 Java 程序。除了 Java Servlet API,Servlet 还可以使用用以扩展和添加到 API 的 Java 类软件包。Servlet 在启用 Java 的 Web 服务器上或应用服务器上运行并扩展了该服务器的能力。Java servlet对于Web服务器就好象Java applet对于Web浏览器。Servlet装入Web服务器并在Web服务器内执行,而applet装入Web浏览器并在Web浏览器内执行。Java Servlet API 定义了一个servlet 和Java使能的服务器之间的一个标准接口,这使得Servlets具有跨服务器平台的特性。
  Servlet 通过创建一个框架来扩展服务器的能力,以提供在 Web 上进行请求和响应服务。当客户机发送请求至服务器时,服务器可以将请求信息发送给 Servlet,并让 Servlet 建立起服务器返回给客户机的响应。 当启动 Web 服务器或客户机第一次请求服务时,可以自动装入 Servlet。装入后, Servlet 继续运行直到其它客户机发出请求。Servlet 的功能涉及范围很广。例如,Servlet 可完成如下功能:

 

(1) 创建并返回一个包含基于客户请求性质的动态内容的完整的 HTML页面。

(2) 创建可嵌入到现有 HTML 页面中的一部分 HTML 页面(HTML 片段)。

(3) 与其它服务器资源(包括数据库和基于 Java 的应用程序)进行通信。

(4) 用多个客户机处理连接,接收多个客户机的输入,并将结果广播到多个客户机上。例如,Servlet 可
以是多参与者的游戏服务器。

(5) 当允许在单连接方式下传送数据的情况下,在浏览器上打开服务器至applet的新连接,并将该连
接保持在打开状态。当允许客户机和服务器简单、高效地执行会话的情况下,applet也可以启动客户浏览器和服务器之间的连接。可以通过定制协议或标准(如 IIOP)进行通信。

(6) 对特殊的处理采用 MIME 类型过滤数据,例如图像转换和服务器端包括(SSI)。

(7) 将定制的处理提供给所有服务器的标准例行程序。例如,Servlet 可以修改如何认证用户。

Servlet 的生命周期

  Servlet 的生命周期始于将它装入 Web 服务器的内存时,并在终止或重新装入 Servlet 时结束。

(1) 初始化
在下列时刻装入 Servlet:
=如果已配置自动装入选项,则在启动服务器时自动装入
=在服务器启动后,客户机首次向 Servlet 发出请求时
=重新装入 Servlet 时
装入 Servlet 后,服务器创建一个 Servlet 实例并且调用 Servlet 的 init() 方法。在初始化阶段,Servlet 初始化参数被传递给 Servlet 配置对象。

(2) 请求处理
对于到达服务器的客户机请求,服务器创建特定于请求的一个"请求"对象和一个"响应"对象。服务器调用 Servlet 的 service() 方法,该方法用于传递"请求"和"响应"对象。service() 方法从"请求"对象获得请求信息、处理该请求并用"响应"对象的方法以将响应传回客户机。service() 方法可以调用其它方法来处理请求,例如 doGet()、doPost() 或其它的方法。

(3) 终止
当服务器不再需要 Servlet, 或重新装入 Servlet 的新实例时,服务器会调用 Servlet 的 destroy() 方法。

  回复  更多评论
  
# re: 面试题 2008-06-18 22:27 | BlueSunshine
1 替换
2 编码转换
3 一个RuntimeException例子  回复  更多评论
  

只有注册用户登录后才能发表评论。


网站导航: