java语言 百分网手机站

Java线程知识笔记

时间:2020-09-16 13:11:12 java语言 我要投稿

Java线程知识笔记

  如果使用得当,线程可以有效地降低程序的开发和维护等成本,同时提升复杂应用程序的性能。具体说,线程的优势有:

Java线程知识笔记

Java线程知识笔记

  1、发挥多处理器的强大能力

  现在,多处理器系统正日益盛行,并且价格不断降低,即时在低端服务器和中断桌面系统中,通常也会采用多个处理器,这种趋势还在进一步加快,因为通过提高时钟频率来提升性能已变得越来越困难,处理器生产厂商都开始转而在单个芯片上放置多个处理器核。

  试想,如果只有单个线程,双核处理器系统上程序只能使用一半的CPU资源,拥有100个处理器的系统上将有99%的资源无法使用。多线程程序则可以同时在多个处理器上执行,如果设计正确,多线程程序可以通过提高处理器资源的利用率来提升系统吞吐率。

  2、在单处理器系统上获得更高的吞吐率

  如果程序是单线程的,那么当程序等待某个同步I/O操作完成时,处理器将处于空闲状态。而在多线程程序中,如果一个线程在等待I/O操作完成,另一个线程可以继续运行,使得程序能在I/O阻塞期间继续运行。

  3、建模的简单性

  通过使用线程,可以将复杂并且异步的工作流进一步分解为一组简单并且同步的工作流,每个工作流在一个单独的线程中运行,并在特定的同步位置进行交互。我们可以通过一些现有框架来实现上述目标,例如Servlet和RMI,框架负责解决一些细节问题,例如请求管理、线程创建、负载平衡,并在正确的时候将请求分发给正确的应用程序组件。

  编写Servlet的开发人员不需要了解多少请求在同一时刻要被处理,也不需要了解套接字的输入流或输出流是否被阻塞,当调用Servlet的service方法来响应Web请求时,可以以同步的方式来处理这个请求,就好像它是一个单线程程序。

  4、异步事件的简化处理

  服务器应用程序在接受多个来自远程客户端的套接字连接请求时,如果为每个连接都分配其各自的线程并且使用同步I/O,那么就会降低这类程序的开发难度。如果某个应用程序对套接字执行读操作而此时还没有数据到来,那么这个读操作将一直阻塞,直到有数据到达。

  在单线程应用程序中,这不仅意味着在处理请求的过程中将停顿,而且还意味着在这个线程被阻塞期间,对所有请求的处理都将停顿。

  为了避免这个问题,单线程服务器应用程序必须使用非阻塞I/O,但是这种I/O的复杂性要远远高于同步I/O,并且很容易出错。然而,如果每个请求都拥有自己的处理线程,那么在处理某个请求时发生的阻塞将不会影响其他请求的处理。

  知识回顾

  进程与线程是常常被提到的两个概念。进程拥有独立的代码段、数据空间,线程共享代码段和数据空间,但有独立的栈空间。线程是操作系统调度的最小单位,通常一个进程会包含一个或多个线程。

  多线程和多进程都可以实现并发处理,如 nginx 使用多进程方式、tomcat 使用多线程方式、Apache 支持混合使用。在 C/C++ 等语言中可以同时使用多进程和多线程,而在 Java 中只能使用多线程。

  在 Java 中,创建线程的唯一方式是创建 Thread 类的实例,调用实例的 start() 方法启动线程。

  Java 线程实现

  在 JDK 1.2 之前,Java 使用用户线程实现 Java 线程,在 JDK 1.2 及之后,Java 基于操作系统原生的线程模型实现 Java 线程。

  使用用户线程( User Thread, UT ) 实现,是指线程建立在用户态空间,线程的建立、同步、调度与销毁都在用户态完成,进程与用户线程之间是1 : N 的对应关系。这种情况下,内核无法知道有多少个用户线程,实现较为复杂。

  使用内核线程实现,是指基于轻量级进程( Light Weight Process, LWP ) 来实现线程。每个轻量级进程都有一个内核线程( Kernel-Level Thread, KLT ) 支持,与内核线程之间是 1 : 1 的对应关系。这种情况下,调度线程时可能需要在内核态和用户态之间进行切换。

  由于轻量级进程需要消耗内核资源,能够支持的线程数量是有限的。

  如在 Windows 和 Linux 系统中,操作系统原生的线程模型是 1 : 1 的对应关系,对于 Sun JDK 来说,一个 Java 线程就对应着一个轻量级进程。

  线程调度与状态

  在 Java中线程的调度方式是抢占式调度,即由系统来负责各个线程的时间分配,并在线程使用完分配的时间后调度下一个线程。任何一个线程都不能独占 CPU 。Java 语言一共设置了 10 个线程优先级,当两个线程同时等待执行时,优先级高的先被调度。

  线程的优先级会被映射到操作系统原生线程上去,但各个操作系统的优先级划分不完全一样,因此两个优先级不同的 Java 线程在操作系统中执行时也可能处于相同的'优先级。

  Java 定义了 5 种线程状态,分别是新建 ( New )、运行 ( Running )、等待 ( Waiting )、限期等待 ( Timed Waiting )、阻塞 ( Blocked ) 和结束 ( Terminated )。任一时刻,线程都处于 5 种状态中的一种,并在各个状态之间切换,如图所示。

  其中,各个状态含义如下:

  新建:创建后未启动;

  运行:对于 Java 来说,线程已经运行,但对于操作系统来说,可能在运行或等待;

  等待:线程等待被其他线程唤醒,如调用了 wait、join 且没有指定超时时间;

  限期等待:线程等待一段时间后被系统唤醒,如调用了 sleep、wait、join 并设置了超时时间;

  阻塞:线程进入同步区域需要与其他线程协调同步,如需要进入 synchronized 区域但其他线程尚未退出此区域;

  结束:run 方法执行完成后,线程结束。

  虚拟机栈

  在 Java 内存模型中,每个虚拟机线程都有自己私有的虚拟机栈。栈与线程同时创建,其中存储的是线程的栈帧 ( Stack Frame )。每个方法的调用,都对应着一个栈帧的入栈和出栈。在栈帧中,存储着局部变量表 ( Local Variable Table )、操作栈 ( Operand Stack )、动态连接 ( Dynamic Linking )、返回地址 ( Return Address ) 和其他附加信息。

  线程的工作内存

  在内存模型中,Java 要求所有的变量都必须存储在主内存中,每个线程拥有自己的工作内存。工作内存中保存了线程需要读写的变量的主内存的副本。线程对变量的读写操作都在工作内存中直接进行,并不会去操作主内存中的内容,主内存与工作内存的同步由虚拟机完成。不同线程不能访问彼此的工作内存,变量值的传递需要经过主内存才能完成。

  Volatile 修饰的变量可以保证变量对所有线程可见,即某个线程修改变量后,其他线程总能立刻读到新值。即便如此,多线程并发时,对 volatile 变量进行自增自减操作也不能保证线程安全。

  总结

  线程在 Java 中只能通过创建 Thread 类的实例来创建。在 JDK 1.2 之后,Java 中的线程基于操作系统原生的线程模型来实现线程。线程的调度方式是抢占式调度,即由系统来负责各个线程的时间分配,并在线程使用完分配的时间后调度下一个线程。Java 定义了 5 种线程状态:新建、运行、等待、限期等待、阻塞和结束。

  每个虚拟机线程都有自己私有的虚拟机栈。栈与线程同时创建,其中存储的是线程的栈帧。每个方法的调用,都对应着一个栈帧的入栈和出栈。每个线程拥有自己的工作内存,工作内存中保存了线程需要读写的变量的主内存的副本。线程对变量的读写操作都在工作内存中直接进行,并不会去操作主内存中的内容,主内存与工作内存的同步由虚拟机完成。

【Java线程知识笔记】相关文章:

java多线程介绍12-07

java多线程教程11-21

什么是java主线程12-07

关于Java多线程介绍11-22

Java线程面试题10-05

Java 5线程池使用09-29

Java线程编程中的主线程详细介绍11-25

java多线程-线程通信实例详细解读11-24

java单线程多线程的实现与方法12-05

JAVA多线程之线程间的通信方式解析11-25