`

java 5.0 多线程编程实践

阅读更多

 Java5增加了新的类库并发集java.util.concurrent,该类库为并发程序提供了丰富的API多线程编程在Java 5中更加容易,灵活。本文通过一个网络服务器模型,来实践Java5的多线程编程,该模型中使用了Java5中的线程池,阻塞队列,可重入锁等,还实践了Callable, Future等接口,并使用了Java 5的另外一个新特性泛型。

  简介

  本文将实现一个网络服务器模型,一旦有客户端连接到该服务器,则启动一个新线程为该连接服务,服务内容为往客户端输送一些字符信息。一个典型的网络服务器模型如下:

  1. 建立监听端口。

  2. 发现有新连接,接受连接,启动线程,执行服务线程。 3. 服务完毕,关闭线程。

  这个模型在大部分情况下运行良好,但是需要频繁的处理用户请求而每次请求需要的服务又是简短的时候,系统会将大量的时间花费在线程的创建销毁。Java 5的线程池克服了这些缺点。通过对重用线程来执行多个任务,避免了频繁线程的创建与销毁开销,使得服务器的性能方面得到很大提高。因此,本文的网络服务器模型将如下:

  1. 建立监听端口,创建线程池。

  2. 发现有新连接,使用线程池来执行服务任务。

  3. 服务完毕,释放线程到线程池。

  下面详细介绍如何使用Java 5的concurrent包提供的API来实现该服务器。

  初始化

  初始化包括创建线程池以及初始化监听端口。创建线程池可以通过调用java.util.concurrent.Executors类里的静态方法newChahedThreadPool或是newFixedThreadPool来创建,也可以通过新建一个java.util.concurrent.ThreadPoolExecutor实例来执行任务。这里我们采用newFixedThreadPool方法来建立线程池。

ExecutorService pool = Executors.newFixedThreadPool(10);


  表示新建了一个线程池,线程池里面有10个线程为任务队列服务。

  使用ServerSocket对象来初始化监听端口。

private static final int PORT = 19527;
serverListenSocket = new ServerSocket(PORT);
serverListenSocket.setReuseAddress(true);
serverListenSocket.setReuseAddress(true);


  服务新连接

  当有新连接建立时,accept返回时,将服务任务提交给线程池执行。

while(true){
 Socket socket = serverListenSocket.accept();
 pool.execute(new ServiceThread(socket));
}


  这里使用线程池对象来执行线程,减少了每次线程创建和销毁的开销。任务执行完毕,线程释放到线程池。

  服务任务

  服务线程ServiceThread维护一个count来记录服务线程被调用的次数。每当服务任务被调用一次时,count的值自增1,因此ServiceThread提供一个increaseCount和getCount的方法,分别将count值自增1和取得该count值。由于可能多个线程存在竞争,同时访问count,因此需要加锁机制,在Java 5之前,我们只能使用synchronized来锁定。Java 5中引入了性能更加粒度更细的重入锁ReentrantLock。我们使用ReentrantLock保证代码线程安全。下面是具体代码:

private static ReentrantLock lock = new ReentrantLock ();
private static int count = 0;
private int getCount(){
 int ret = 0;
 try{
  lock.lock();
  ret = count;
 }finally{
  lock.unlock();
 }
 return ret;
}
private void increaseCount(){
 try{
  lock.lock();
  ++count;
 }finally{
  lock.unlock();
 }
}


  服务线程在开始给客户端打印一个欢迎信息,

increaseCount();
int curCount = getCount();
helloString = "hello, id = " + curCount+"\r\n";
dos = new DataOutputStream(connectedSocket.getOutputStream());
dos.write(helloString.getBytes());


  然后使用ExecutorService的submit方法提交一个Callable的任务,返回一个Future接口的引用。这种做法对费时的任务非常有效,submit任务之后可以继续执行下面的代码,然后在适当的位置可以使用Future的get方法来获取结果,如果这时候该方法已经执行完毕,则无需等待即可获得结果,如果还在执行,则等待到运行完毕。

ExecutorService executor = Executors.newSingleThreadExecutor();
Future <String> future = executor.submit(new TimeConsumingTask());
dos.write("let's do soemthing other".getBytes());
String result = future.get();
dos.write(result.getBytes());


  其中TimeConsumingTask实现了Callable接口

class TimeConsumingTask implements Callable <String>{
 public String call() throws Exception {
  System.out.println("It's a time-consuming task, you'd better retrieve your result in the furture");
  return "ok, here's the result: It takes me lots of time to produce this result";
 }
}


  这里使用了Java 5的另外一个新特性泛型,声明TimeConsumingTask的时候使用了String做为类型参数。必须实现Callable接口的call函数,其作用类似与Runnable中的run函数,在call函数里写入要执行的代码,其返回值类型等同于在类声明中传入的类型值。在这段程序中,我们提交了一个Callable的任务,然后程序不会堵塞,而是继续执行dos.write("let's do soemthing other".getBytes());当程序执行到String result = future.get()时如果call函数已经执行完毕,则取得返回值,如果还在执行,则等待其执行完毕。
服务器端的完整实现

  服务器端的完整实现代码如下:

package com.andrew;

import java.io.DataOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

public class Server {
 private static int produceTaskSleepTime = 100;
 private static int consumeTaskSleepTime = 1200;
 private static int produceTaskMaxNumber = 100;
 private static final int CORE_POOL_SIZE = 2;
 private static final int MAX_POOL_SIZE = 100;
 private static final int KEEPALIVE_TIME = 3;
 private static final int QUEUE_CAPACITY = (CORE_POOL_SIZE + MAX_POOL_SIZE) / 2;
 private static final TimeUnit TIME_UNIT = TimeUnit.SECONDS;
 private static final String HOST = "127.0.0.1";
 private static final int PORT = 19527;
 private BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<Runnable>(QUEUE_CAPACITY);
 //private ThreadPoolExecutor serverThreadPool = null;
 private ExecutorService pool = null;
 private RejectedExecutionHandler rejectedExecutionHandler = new ThreadPoolExecutor.DiscardOldestPolicy();
 private ServerSocket serverListenSocket = null;
 private int times = 5;
 public void start() {
  // You can also init thread pool in this way.
  /*serverThreadPool = new ThreadPoolExecutor(CORE_POOL_SIZE,
  MAX_POOL_SIZE, KEEPALIVE_TIME, TIME_UNIT, workQueue,
  rejectedExecutionHandler);*/
  pool = Executors.newFixedThreadPool(10);
  try {
   serverListenSocket = new ServerSocket(PORT);
   serverListenSocket.setReuseAddress(true);

   System.out.println("I'm listening");
   while (times-- > 0) {
    Socket socket = serverListenSocket.accept();
    String welcomeString = "hello";
    //serverThreadPool.execute(new ServiceThread(socket, welcomeString));
    pool.execute(new ServiceThread(socket));
   }
  } catch (IOException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  cleanup();
 }

 public void cleanup() {
  if (null != serverListenSocket) {
   try {
    serverListenSocket.close();
   } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
  }
  //serverThreadPool.shutdown();
  pool.shutdown();
 }

 public static void main(String args[]) {
  Server server = new Server();
  server.start();
 }
}

class ServiceThread implements Runnable, Serializable {
 private static final long serialVersionUID = 0;
 private Socket connectedSocket = null;
 private String helloString = null;
 private static int count = 0;
 private static ReentrantLock lock = new ReentrantLock();

 ServiceThread(Socket socket) {
  connectedSocket = socket;
 }

 public void run() {
  increaseCount();
  int curCount = getCount();
  helloString = "hello, id = " + curCount + "\r\n";

  ExecutorService executor = Executors.newSingleThreadExecutor();
  Future<String> future = executor.submit(new TimeConsumingTask());

  DataOutputStream dos = null;
  try {
   dos = new DataOutputStream(connectedSocket.getOutputStream());
   dos.write(helloString.getBytes());
   try {
    dos.write("let's do soemthing other.\r\n".getBytes());
    String result = future.get();
    dos.write(result.getBytes());
   } catch (InterruptedException e) {
    e.printStackTrace();
   } catch (ExecutionException e) {
    e.printStackTrace();
   }
  } catch (IOException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } finally {
   if (null != connectedSocket) {
    try {
     connectedSocket.close();
    } catch (IOException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    }
   }
   if (null != dos) {
    try {
     dos.close();
    } catch (IOException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    }
   }
   executor.shutdown();
  }
 }

 private int getCount() {
  int ret = 0;
  try {
   lock.lock();
   ret = count;
  } finally {
   lock.unlock();
  }
  return ret;
 }

 private void increaseCount() {
  try {
   lock.lock();
   ++count;
  } finally {
   lock.unlock();
  }
 }
}

class TimeConsumingTask implements Callable<String> {
 public String call() throws Exception {
  System.out.println("It's a time-consuming task, you'd better retrieve your result in the furture");
  return "ok, here's the result: It takes me lots of time to produce this result";
 }

}


  运行程序

  运行服务端,客户端只需使用telnet 127.0.0.1 19527 即可看到信息

分享到:
评论

相关推荐

    Java5.0多线程编程实践.pdf

    本文将结合Java 5.0中的新特性,讲解如何在Java中实现多线程编程实践。 在Java 5.0之前,多线程的实现主要依赖于`java.lang.Thread`类和`synchronized`关键字。尽管这些机制能够满足基本的多线程编程需求,但在复杂...

    Java 5_0 多线程编程实践

    ### Java 5.0 多线程编程实践 在Java 5.0版本中,多线程编程得到了显著增强,特别是在引入了`java.util.concurrent`包之后,为开发者提供了更为丰富的API来处理并发问题。本篇文章将深入探讨如何在Java 5.0中有效地...

    Java API_5.0中文版

    8. **多线程(Multithreading)**:Java API提供了Thread类和Runnable接口来支持多线程编程,理解和掌握线程的同步、互斥、线程池等概念对于开发高并发应用程序至关重要。 9. **I/O流(Input/Output Streams)**:...

    Java 5.0 Tiger程序高手秘笈.rar

    Java 5.0对并发编程进行了大量优化,包括新增了java.util.concurrent包,提供了线程池、并发容器(如ConcurrentHashMap)以及Future和Callable接口,使得多线程编程更加高效和易于管理。 通过深入学习和实践《Java ...

    Java 5.0 Tiger程序高手秘笈(PDF) Java.rar

    除此之外,书中还可能涉及了Java 5.0中的多线程和并发编程,包括新的并发API如`java.util.concurrent`包,以及线程池、Future和Callable接口等概念。这些内容对于构建高性能、高并发的Web应用至关重要。 总的来说,...

    Java5.0 Tiger程序高手秘笈(含源码)

    9. **并发工具**:Java 5.0引入了`java.util.concurrent`包,提供了线程池、并发容器、同步工具类等,帮助开发者更好地管理多线程和并发编程。 10. **定时器和定时任务**:`java.util.Timer`和`java.util.TimerTask...

    良葛格java5.0的学习笔记

    10. **Concurrency Updates(并发更新)**:Java 5.0增强了多线程编程的支持,如引入了`java.util.concurrent`包,包含Semaphore、ExecutorService、Future、Callable等类,提供了更高效的线程管理和任务调度。...

    Java多线程编程实战指南+设计模式篇(全部)

    总之,“Java多线程编程实战指南+设计模式篇(全部)”是一份宝贵的资源,它将帮助读者深入理解Java多线程编程的各个方面,以及如何利用设计模式解决实际问题。学习并掌握这些知识,对于提升Java程序员的专业技能和...

    《Java5.0 Tiger》书籍源码

    《Java 5.0 Tiger》是一本针对Java编程语言5.0版本的权威指南,尤其对Tiger(Java 5.0的代号)引入的新特性进行了深入的讲解。这本书的源码是学习和理解Java 5.0新特性的宝贵资源,包括泛型、枚举、自动装箱与拆箱、...

    JAVA 5.0 TIGER程序高手秘笈

    在并发处理方面,Java 5.0引入了并发工具类(java.util.concurrent),如ExecutorService、Future、Semaphore等,这些工具简化了多线程编程,提高了程序的可维护性和性能。还有改进的synchronized关键字,支持基于...

    Java5.0 Tiger全书代码.rar

    这个"Java5.0 Tiger全书代码.rar"压缩包包含的是《Java5.0 Tiger程序员高手秘笈》这本书的配套源码,这是一本深受Java开发者喜爱的进阶指南,旨在帮助读者深入理解Java 5.0的新特性和最佳实践。 首先,Java 5.0的...

    java5.0 认证指南

    Java 5.0认证指南不仅涵盖了Java语言的基础知识,还深入探讨了高级主题,如面向对象设计原则、多线程编程、网络编程等。对于希望获得SCJP或SCJD认证的开发者来说,这是一份不可或缺的学习资源。通过系统地学习和实践...

    JAVA数据结构和算法 JAVA 5.0

    10. **练习题的扩展**:书中包含了大量练习题,总数达到了670道,涵盖了强化训练、创新思维以及项目实践等多个层面,新增加的练习题包括维护游戏高分列表、评估后缀表达式和中缀表达式、最小最大值游戏树评估等。...

    Java多线程编辑核心技术

    除了上述核心概念和工具类,Java 5.0引入了并发工具类如CountDownLatch、CyclicBarrier、Semaphore等,它们为多线程编程提供了更细致的控制。比如,CountDownLatch允许一个或多个线程等待其他线程完成操作,...

    JAVA.5.0.TIGER程序高手秘笈

    13. **线程并发库**:Java 5.0引入了java.util.concurrent包,包含各种并发工具类,如ExecutorService、Future、Semaphore等,使得多线程编程更加高效和安全。 14. **枚举类的枚举常量间的关系**:书中可能还会讨论...

    良葛格Java JDK 5.0学习笔记

    10. **并发编程改进**:Java 5.0引入了`java.util.concurrent`包,包含了线程池、Future、Callable、BlockingQueue等并发工具,提升了多线程环境下的编程效率和安全性。 这些是Java JDK 5.0的主要特性,它们不仅...

    java多线程和锁,用于学习参考

    Java多线程与锁是Java并发编程中的核心概念,它们在构建高效、可扩展的并发应用程序中起着至关重要的作用。下面将详细讲解这两个主题。 首先,Java中的多线程是指程序执行时可以同时进行多个任务。这得益于操作系统...

    Java2 Tutorial-5.0

    并发处理方面,增加了并发工具类如`ExecutorService`,`Semaphore`和`CountDownLatch`等,使得多线程编程更加容易和高效。 在面向对象设计方面,Java 5.0支持注解(Annotations),这是一种元数据形式,可以用来...

Global site tag (gtag.js) - Google Analytics