`

java 并发小结资料整理

 
阅读更多
预备知识:

线程创建
通过例示从 Thread 获得的对象并调用 Thread.start() 方法来创建线程。可以用两种方法创建线程:通过扩展 Thread 和覆盖 run() 方法,或者通过实现 Runnable 接口和使用 Thread(Runnable) 构造函数:
class WorkerThread extends Thread {
  public void run() { /* do work */ }
}
Thread t = new WorkerThread();
t.start();
或者:
Thread t = new Thread(new Runnable() {
  public void run() { /* do work */ }
}
t.start();


如何不对任务进行管理
大多数服务器应用程序(如 Web 服务器、POP 服务器、数据库服务器或文件服务器)代表远程客户机处理请求,这些客户机通常使用 socket 连接到服务器。对于每个请求,通常要进行少量处理(获得该文件的代码块,并将其发送回 socket),但是可能会有大量(且不受限制)的客户机请求服务。
用于构建服务器应用程序的简单化模型会为每个请求创建新的线程。下列代码段实现简单的 Web 服务器,它接受端口 80 的 socket 连接,并创建新的线程来处理请求。不幸的是,该代码不是实现 Web 服务器的好方法,因为在重负载条件下它将失败,停止整台服务器。
class UnreliableWebServer {
  public static void main(String[] args) {
    ServerSocket socket = new ServerSocket(80);
      while (true) {
      final Socket connection = socket.accept();
      Runnable r = new Runnable() {
        public void run() {
          handleRequest(connection);
        }
      };
      // Don't do this!
      new Thread(r).start();
    }
  }
}
当服务器被请求吞没时,UnreliableWebServer 类不能很好地处理这种情况。每次有请求时,就会创建新的类。根据操作系统和可用内存,可以创建的线程数是有限的。
不幸的是,您通常不知道限制是多少 -- 只有当应用程序因为 OutOfMemoryError 而崩溃时才发现。
如果足够快地在这台服务器上抛出请求的话,最终其中一个线程创建将失败,生成的 Error 会关闭整个应用程序。当一次仅能有效支持很少线程时,没有必要创建上千个
线程,无论如何,这样使用资源可能会损害性能。创建线程会使用相当一部分内存,其中包括有两个堆栈(Java 和 C),以及每线程数据结构。如果创建过多线程,其中
每个线程都将占用一些 CPU 时间,结果将使用许多内存来支持大量线程,每个线程都运行得很慢。这样就无法很好地使用计算资源。

使用线程池解决问题
为任务创建新的线程并不一定不好,但是如果创建任务的频率高,而任务平均持续时间低,我们可以看到每项任务创建一个新的线程将产生性能(如果负载不可预知,还有稳定性)问题。
如果不是每项任务创建一个新的线程,则服务器应用程序必须采取一些方法来限制一次可以处理的请求数。这意味着每次需要启动新的任务时,它不能仅调用下列代码。
new Thread(runnable).start()
管理一大组小任务的标准机制是组合工作队列和线程池。工作队列就是要处理的任务的队列,前面描述的 Queue 类完全适合。线程池是线程的集合,每个线程都提取公用工作队列。当一个工作线程完成任务处理后,它会返回队列,查看是否有其他任务需要处理。如果有,它会转移到下一个任务,并开始处理。
线程池为线程生命周期间接成本问题和资源崩溃问题提供了解决方案。通过对多个任务重新使用线程,创建线程的间接成本将分布到多个任务中。作为一种额外好处,因为请求到达时,线程已经存在,从而可以消除由创建线程引起的延迟。因此,可以立即处理请求,使应用程序更易响应。而且,通过正确调整线程池中的线程数,可以强制超出特定限制的任何请求等待,直到有线程可以处理它,它们等待时所消耗的资源要少于使用额外线程所消耗的资源,这样可以防止资源崩溃。

Executor 框架
java.util.concurrent 包中包含灵活的线程池实现,但是更重要的是,它包含用于管理实现 Runnable 的任务的执行的整个框架。该框架称为 Executor 框架。
Executor 接口相当简单。它描述将运行 Runnable 的对象:
public interface Executor {
  void execute(Runnable command);
}
任务运行于哪个线程不是由该接口指定的,这取决于使用的 Executor 的实现。
java.util.concurrent 中的大多数 Executor 实现还实现 ExecutorService 接口,这是对 Executor 的扩展,它还管理执行服务的生命周期。这使它们更易于管理,并向生命可能比单独 Executor 的生命更长的应用程序提供服务。
public interface ExecutorService extends Executor {

  void shutdown();

  List shutdownNow();

  boolean isShutdown();

  boolean isTerminated();

  boolean awaitTermination(long timeout,

                           TimeUnit unit);

  // other convenience methods for submitting tasks

}
java.util.concurrent 包含多个 Executor 实现,每个实现都实现不同的执行策略。什么是执行策略?执行策略定义何时在哪个线程中运行任务,执行任务可能消耗的资源级别(线程、内存等等),以及如果执行程序超载该怎么办。
执行程序通常通过工厂方法示例,而不是通过构造函数。Executors 类包含用于构造许多不同类型的 Executor 实现的静态工厂方法:
1. Executors.newCachedThreadPool() 创建不限制大小的线程池,但是当以前创建的线程可以使用时将重新使用那些线程。如果没有现有线程可用,
将创建新的线程并将其添加到池中。使用不到 60 秒的线程将终止并从缓存中删除。
2. Executors.newFixedThreadPool(int n) 创建线程池,其重新使用在不受限制的队列之外运行的固定线程组。在关闭前,所有线程都会因为执行
过程中的失败而终止,如果需要执行后续任务,将会有新的线程来代替这些线程。
3. Executors.newSingleThreadExecutor() 创建单一工作线程,与 Swing 事件线程非常相似。保证顺序执行任务,在任何给定时间,不会有多个任务处于活动状态。

之前的例子可以做简单调整,只需将 Thread.start() 调用替换为向 Executor 提交任务即可:
class ReliableWebServer {

  Executor pool = Executors.newFixedThreadPool(7);

    public static void main(String[] args) {

    ServerSocket socket = new ServerSocket(80);

      while (true) {

      final Socket connection = socket.accept();

      Runnable r = new Runnable() {

        public void run() {

          handleRequest(connection);

        }

      };

      pool.execute(r);

    }

  }

}


转载链接:http://blog.csdn.net/aalansehaiyang52/article/details/8971992
分享到:
评论

相关推荐

    Java 垃圾回收小结(一)

    本文将对Java垃圾回收进行小结,探讨其基本原理、类型以及常见算法。 1. 基本原理: Java中的内存分为堆(Heap)和栈(Stack)两部分,垃圾回收主要关注堆内存。当一个对象不再被任何引用指向时,它被视为可回收的...

    毕向东Java笔记

    #### 小结 本章介绍了Java的基础知识,包括Java的特点、运行机制、开发环境的搭建、注释的使用、标识符的规则、变量的概念及分类等。这些内容对于Java初学者来说至关重要,是学习Java编程的第一步。接下来的章节将...

    Java线程安全问题小结_动力节点Java学院整理

    Java线程安全问题主要涉及到并发环境下多个线程对共享资源的访问和修改,这可能导致数据不一致、死锁等问题。Java内存模型(JMM)是Java语言为了保证跨平台的并发正确性而设定的一套规范,它规定了线程如何访问和...

    Java虚拟机

    1.7 本章小结 第二部分 自动内存管理机制 第2章 Java内存区域与内存溢出异常 2.1 概述 2.2 运行时数据区域 2.2.1 程序计数器 2.2.2 Java虚拟机栈 2.2.3 本地方法栈 2.2.4 Java堆 2.2.5 方法区 2.2.6 运行...

    (最新整理)Java聊天室系统程序设计实验报告.pdf

    **第六部分:小结** 这部分总结了实验过程中的挑战、解决方案以及实验结果,评估了系统性能和用户体验,并提出了改进的建议。 **第七部分:参考文献** 列出在设计和实现过程中参考的相关书籍、文章或在线资源,为...

    java 试题 基础

    ### 小结 本文总结了Java基础中的一些核心知识点,包括类型系统、数组与集合、异常处理、Servlet与CGI、EJB、并发控制以及内存管理等方面的内容。这些知识点对于理解和掌握Java语言的基本原理至关重要,也是面试中常...

    高性能高并发服务器架构大全

    2.4 网络层架构小结 214 3.1 第四层交换简介 214 3.2 硬件实现 215 3.3 软件实现 215  网站架构的高性能和可扩展性 233  资料收集:高并发 高性能 高扩展性 Web 2.0 站点架构设计及优化策略 243  ...

    心得.rar心得.rar

    1. **Java线程小结**:这部分内容可能涉及多线程编程的基础知识,如线程的创建、同步机制(synchronized、Lock等)、并发工具类(Semaphore、CountDownLatch、CyclicBarrier等),以及线程安全问题和解决方案。...

    Java_TCPIP_Socket编程(中文版)

    - **Selector小结**:总结使用`Selector`的关键步骤和最佳实践。 - **数据报(UDP)信道**:描述如何使用`DatagramChannel`进行UDP通信。 #### 第6章 深入剖析 - **缓冲和TCP**:深入探讨TCP协议中的缓冲机制...

    hibernate笔记整理

    #### 六、小结 通过对上述代码和映射文件的分析,我们可以了解到在Hibernate中实现一对多的双向关联的基本步骤。通过正确配置`inverse`属性,可以有效地避免并发问题和循环更新的问题,确保数据的一致性和完整性。...

    SQLServer2008查询性能优化 2/2

    1.6 小结 14 第2章 系统性能分析 15 2.1 性能监视器工具 15 2.2 动态管理视图 17 2.3 硬件资源瓶颈 18 2.3.1 识别瓶颈 18 2.3.2 瓶颈解决方案 19 2.4 内存瓶颈分析 19 2.4.1 SQL Server内存管理 20 2.4.2 ...

    SQLServer2008查询性能优化 1/2

    1.6 小结 14 第2章 系统性能分析 15 2.1 性能监视器工具 15 2.2 动态管理视图 17 2.3 硬件资源瓶颈 18 2.3.1 识别瓶颈 18 2.3.2 瓶颈解决方案 19 2.4 内存瓶颈分析 19 2.4.1 SQL Server内存管理 20 2.4.2 ...

    C语言 填空题整理

    不同的隔离级别对应不同的并发控制策略,以平衡并发性和一致性。 ### 51. 数据的查询 - **选项分析**:数据查询是指从数据库中检索满足特定条件的数据。查询可以基于SQL语句或者图形化界面进行。 ### 52. 栈和...

    一个优秀IT开发人员必看的技术文档

    在小结中,可能会讨论不同类型的负载均衡器,如Nginx、HAProxy等,以及它们的配置、优势和适用场景。 十一、Hadoop部署脚本 Hadoop是一个开源框架,它允许使用简单的编程模型在跨计算机集群存储和处理大量数据。...

    JVM学习札记

    ### 小结 本文详细介绍了JVM的基本运行机制、调试参数、垃圾收集算法以及监控和锁的应用等内容。深入理解这些知识点对于高效开发和维护Java应用程序至关重要。通过对JVM的工作原理有一个清晰的认识,开发者可以更好...

    软件技术专业技能抽查题库

    - **题目概述**:这是一款面向儿童的智力开发游戏,通过各种小游戏提升孩子的逻辑思维能力和创造力。 - **关键技术点**: - 游戏设计,如何让游戏既有趣又有教育意义。 - 界面友好,考虑到儿童用户的使用习惯。 -...

Global site tag (gtag.js) - Google Analytics